Fix compilation issues on free version, add more extensive hardware accelleration support for video

visualiser-refactor
James H Ball 2025-04-27 11:36:37 +01:00
rodzic 231e1d8234
commit a125abfa7f
15 zmienionych plików z 962 dodań i 512 usunięć

Wyświetl plik

@ -158,12 +158,15 @@ void MainComponent::updateFileLabel() {
showRightArrow = audioProcessor.getCurrentFileIndex() < audioProcessor.numFiles() - 1; showRightArrow = audioProcessor.getCurrentFileIndex() < audioProcessor.numFiles() - 1;
{ {
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock); juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
if (audioProcessor.objectServerRendering) { if (audioProcessor.isSyphonInputActive()) {
fileLabel.setText("Rendering from Blender", juce::dontSendNotification);
} else if (audioProcessor.isSyphonInputActive()) {
fileLabel.setText(audioProcessor.getSyphonSourceName(), juce::dontSendNotification); fileLabel.setText(audioProcessor.getSyphonSourceName(), juce::dontSendNotification);
} else if (audioProcessor.getCurrentFileIndex() == -1) { } else
#endif
if (audioProcessor.objectServerRendering) {
fileLabel.setText("Rendering from Blender", juce::dontSendNotification);
}else if (audioProcessor.getCurrentFileIndex() == -1) {
fileLabel.setText("No file open", juce::dontSendNotification); fileLabel.setText("No file open", juce::dontSendNotification);
} else { } else {
fileLabel.setText(audioProcessor.getCurrentFileName(), juce::dontSendNotification); fileLabel.setText(audioProcessor.getCurrentFileName(), juce::dontSendNotification);

Wyświetl plik

@ -526,8 +526,8 @@ void OscirenderAudioProcessorEditor::openVisualiserSettings() {
visualiserSettingsWindow.toFront(true); visualiserSettingsWindow.toFront(true);
} }
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
void OscirenderAudioProcessorEditor::openSyphonInputDialog() { void OscirenderAudioProcessorEditor::openSyphonInputDialog() {
#if JUCE_MAC || JUCE_WINDOWS
SyphonInputSelectorComponent* selector = nullptr; SyphonInputSelectorComponent* selector = nullptr;
{ {
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock); juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
@ -548,7 +548,6 @@ void OscirenderAudioProcessorEditor::openSyphonInputDialog() {
options.useNativeTitleBar = true; options.useNativeTitleBar = true;
options.resizable = false; options.resizable = false;
options.launchAsync(); options.launchAsync();
#endif
} }
void OscirenderAudioProcessorEditor::onSyphonInputSelected(const juce::String& server, const juce::String& app) { void OscirenderAudioProcessorEditor::onSyphonInputSelected(const juce::String& server, const juce::String& app) {
@ -560,3 +559,4 @@ void OscirenderAudioProcessorEditor::onSyphonInputDisconnected() {
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock); juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
audioProcessor.disconnectSyphonInput(); audioProcessor.disconnectSyphonInput();
} }
#endif

Wyświetl plik

@ -7,19 +7,18 @@
*/ */
#include "PluginProcessor.h" #include "PluginProcessor.h"
#include "PluginEditor.h" #include "PluginEditor.h"
#include "parser/FileParser.h"
#include "parser/FrameProducer.h"
#include "audio/VectorCancellingEffect.h"
#include "audio/DistortEffect.h"
#include "audio/SmoothEffect.h"
#include "audio/BitCrushEffect.h" #include "audio/BitCrushEffect.h"
#include "audio/BulgeEffect.h" #include "audio/BulgeEffect.h"
#include "audio/DistortEffect.h"
#include "audio/SmoothEffect.h"
#include "audio/VectorCancellingEffect.h"
#include "parser/FileParser.h"
#include "parser/FrameProducer.h"
#if JUCE_MAC || JUCE_WINDOWS #if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
#include "SyphonFrameGrabber.h" #include "img/ImageParser.h"
#include "img/ImageParser.h"
#include "../modules/juce_sharedtexture/SharedTexture.h"
#endif #endif
//============================================================================== //==============================================================================
@ -28,111 +27,103 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
std::make_shared<BitCrushEffect>(), std::make_shared<BitCrushEffect>(),
new osci::EffectParameter("Bit Crush", "Limits the resolution of points drawn to the screen, making the object look pixelated, and making the audio sound more 'digital' and distorted.", "bitCrush", VERSION_HINT, 0.6, 0.0, 1.0) new osci::EffectParameter("Bit Crush", "Limits the resolution of points drawn to the screen, making the object look pixelated, and making the audio sound more 'digital' and distorted.", "bitCrush", VERSION_HINT, 0.6, 0.0, 1.0)));
));
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
std::make_shared<BulgeEffect>(), std::make_shared<BulgeEffect>(),
new osci::EffectParameter("Bulge", "Applies a bulge that makes the centre of the image larger, and squishes the edges of the image. This applies a distortion to the audio.", "bulge", VERSION_HINT, 0.5, 0.0, 1.0) new osci::EffectParameter("Bulge", "Applies a bulge that makes the centre of the image larger, and squishes the edges of the image. This applies a distortion to the audio.", "bulge", VERSION_HINT, 0.5, 0.0, 1.0)));
));
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
std::make_shared<VectorCancellingEffect>(), std::make_shared<VectorCancellingEffect>(),
new osci::EffectParameter("Vector Cancelling", "Inverts the audio and image every few samples to 'cancel out' the audio, making the audio quiet, and distorting the image.", "vectorCancelling", VERSION_HINT, 0.1111111, 0.0, 1.0) new osci::EffectParameter("Vector Cancelling", "Inverts the audio and image every few samples to 'cancel out' the audio, making the audio quiet, and distorting the image.", "vectorCancelling", VERSION_HINT, 0.1111111, 0.0, 1.0)));
));
toggleableEffects.push_back(std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
return input * osci::Point(values[0], values[1], values[2]);
}, std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Scale X", "Scales the object in the horizontal direction.", "scaleX", VERSION_HINT, 1.0, -5.0, 5.0),
new osci::EffectParameter("Scale Y", "Scales the object in the vertical direction.", "scaleY", VERSION_HINT, 1.0, -5.0, 5.0),
new osci::EffectParameter("Scale Z", "Scales the depth of the object.", "scaleZ", VERSION_HINT, 1.0, -5.0, 5.0),
}
));
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
int flip = index % 2 == 0 ? 1 : -1; return input * osci::Point(values[0], values[1], values[2]);
osci::Point jitter = osci::Point(flip * values[0], flip * values[1], flip * values[2]); },
return input + jitter; std::vector<osci::EffectParameter*>{
}, std::vector<osci::EffectParameter*>{ new osci::EffectParameter("Scale X", "Scales the object in the horizontal direction.", "scaleX", VERSION_HINT, 1.0, -5.0, 5.0),
new osci::EffectParameter("Scale Y", "Scales the object in the vertical direction.", "scaleY", VERSION_HINT, 1.0, -5.0, 5.0),
new osci::EffectParameter("Scale Z", "Scales the depth of the object.", "scaleZ", VERSION_HINT, 1.0, -5.0, 5.0),
}));
toggleableEffects.push_back(std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
int flip = index % 2 == 0 ? 1 : -1;
osci::Point jitter = osci::Point(flip * values[0], flip * values[1], flip * values[2]);
return input + jitter;
},
std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Distort X", "Distorts the image in the horizontal direction by jittering the audio sample being drawn.", "distortX", VERSION_HINT, 0.0, 0.0, 1.0), new osci::EffectParameter("Distort X", "Distorts the image in the horizontal direction by jittering the audio sample being drawn.", "distortX", VERSION_HINT, 0.0, 0.0, 1.0),
new osci::EffectParameter("Distort Y", "Distorts the image in the vertical direction by jittering the audio sample being drawn.", "distortY", VERSION_HINT, 0.0, 0.0, 1.0), new osci::EffectParameter("Distort Y", "Distorts the image in the vertical direction by jittering the audio sample being drawn.", "distortY", VERSION_HINT, 0.0, 0.0, 1.0),
new osci::EffectParameter("Distort Z", "Distorts the depth of the image by jittering the audio sample being drawn.", "distortZ", VERSION_HINT, 0.1, 0.0, 1.0), new osci::EffectParameter("Distort Z", "Distorts the depth of the image by jittering the audio sample being drawn.", "distortZ", VERSION_HINT, 0.1, 0.0, 1.0),
} }));
));
auto rippleEffect = std::make_shared<osci::Effect>( auto rippleEffect = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
double phase = values[1] * std::numbers::pi; double phase = values[1] * std::numbers::pi;
double distance = 100 * values[2] * (input.x * input.x + input.y * input.y); double distance = 100 * values[2] * (input.x * input.x + input.y * input.y);
input.z += values[0] * std::sin(phase + distance); input.z += values[0] * std::sin(phase + distance);
return input; return input;
}, std::vector<osci::EffectParameter*>{ },
std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Ripple Depth", "Controls how large the ripples applied to the image are.", "rippleDepth", VERSION_HINT, 0.2, 0.0, 1.0), new osci::EffectParameter("Ripple Depth", "Controls how large the ripples applied to the image are.", "rippleDepth", VERSION_HINT, 0.2, 0.0, 1.0),
new osci::EffectParameter("Ripple Phase", "Controls the position of the ripple. Animate this to see a moving ripple effect.", "ripplePhase", VERSION_HINT, 0.0, -1.0, 1.0), new osci::EffectParameter("Ripple Phase", "Controls the position of the ripple. Animate this to see a moving ripple effect.", "ripplePhase", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Ripple Amount", "Controls how many ripples are applied to the image.", "rippleAmount", VERSION_HINT, 0.1, 0.0, 1.0), new osci::EffectParameter("Ripple Amount", "Controls how many ripples are applied to the image.", "rippleAmount", VERSION_HINT, 0.1, 0.0, 1.0),
} });
); rippleEffect->getParameter("ripplePhase")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);
rippleEffect->getParameter("ripplePhase")->lfo->setUnnormalisedValueNotifyingHost((int) osci::LfoType::Sawtooth);
toggleableEffects.push_back(rippleEffect); toggleableEffects.push_back(rippleEffect);
auto rotateEffect = std::make_shared<osci::Effect>( auto rotateEffect = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
input.rotate(values[0] * std::numbers::pi, values[1] * std::numbers::pi, values[2] * std::numbers::pi); input.rotate(values[0] * std::numbers::pi, values[1] * std::numbers::pi, values[2] * std::numbers::pi);
return input; return input;
}, std::vector<osci::EffectParameter*>{ },
std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Rotate X", "Controls the rotation of the object in the X axis.", "rotateX", VERSION_HINT, 0.0, -1.0, 1.0), new osci::EffectParameter("Rotate X", "Controls the rotation of the object in the X axis.", "rotateX", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Rotate Y", "Controls the rotation of the object in the Y axis.", "rotateY", VERSION_HINT, 0.0, -1.0, 1.0), new osci::EffectParameter("Rotate Y", "Controls the rotation of the object in the Y axis.", "rotateY", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Rotate Z", "Controls the rotation of the object in the Z axis.", "rotateZ", VERSION_HINT, 0.0, -1.0, 1.0), new osci::EffectParameter("Rotate Z", "Controls the rotation of the object in the Z axis.", "rotateZ", VERSION_HINT, 0.0, -1.0, 1.0),
} });
); rotateEffect->getParameter("rotateY")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);
rotateEffect->getParameter("rotateY")->lfo->setUnnormalisedValueNotifyingHost((int) osci::LfoType::Sawtooth);
rotateEffect->getParameter("rotateY")->lfoRate->setUnnormalisedValueNotifyingHost(0.2); rotateEffect->getParameter("rotateY")->lfoRate->setUnnormalisedValueNotifyingHost(0.2);
toggleableEffects.push_back(rotateEffect); toggleableEffects.push_back(rotateEffect);
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
return input + osci::Point(values[0], values[1], values[2]); return input + osci::Point(values[0], values[1], values[2]);
}, std::vector<osci::EffectParameter*>{ },
std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Translate X", "Moves the object horizontally.", "translateX", VERSION_HINT, 0.0, -1.0, 1.0), new osci::EffectParameter("Translate X", "Moves the object horizontally.", "translateX", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Translate Y", "Moves the object vertically.", "translateY", VERSION_HINT, 0.0, -1.0, 1.0), new osci::EffectParameter("Translate Y", "Moves the object vertically.", "translateY", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Translate Z", "Moves the object away from the camera.", "translateZ", VERSION_HINT, 0.0, -1.0, 1.0), new osci::EffectParameter("Translate Z", "Moves the object away from the camera.", "translateZ", VERSION_HINT, 0.0, -1.0, 1.0),
} }));
));
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
double length = 10 * values[0] * input.magnitude(); double length = 10 * values[0] * input.magnitude();
double newX = input.x * std::cos(length) - input.y * std::sin(length); double newX = input.x * std::cos(length) - input.y * std::sin(length);
double newY = input.x * std::sin(length) + input.y * std::cos(length); double newY = input.x * std::sin(length) + input.y * std::cos(length);
return osci::Point(newX, newY, input.z); return osci::Point(newX, newY, input.z);
}, std::vector<osci::EffectParameter*>{ },
std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Swirl", "Swirls the image in a spiral pattern.", "swirl", VERSION_HINT, 0.3, -1.0, 1.0), new osci::EffectParameter("Swirl", "Swirls the image in a spiral pattern.", "swirl", VERSION_HINT, 0.3, -1.0, 1.0),
} }));
));
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
std::make_shared<SmoothEffect>(), std::make_shared<SmoothEffect>(),
new osci::EffectParameter("Smoothing", "This works as a low-pass frequency filter that removes high frequencies, making the image look smoother, and audio sound less harsh.", "smoothing", VERSION_HINT, 0.75, 0.0, 1.0) new osci::EffectParameter("Smoothing", "This works as a low-pass frequency filter that removes high frequencies, making the image look smoother, and audio sound less harsh.", "smoothing", VERSION_HINT, 0.75, 0.0, 1.0)));
));
std::shared_ptr<osci::Effect> wobble = std::make_shared<osci::Effect>( std::shared_ptr<osci::Effect> wobble = std::make_shared<osci::Effect>(
wobbleEffect, wobbleEffect,
std::vector<osci::EffectParameter*>{ std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Wobble Amount", "Adds a sine wave of the prominent frequency in the audio currently playing. The sine wave's frequency is slightly offset to create a subtle 'wobble' in the image. Increasing the slider increases the strength of the wobble.", "wobble", VERSION_HINT, 0.3, 0.0, 1.0), new osci::EffectParameter("Wobble Amount", "Adds a sine wave of the prominent frequency in the audio currently playing. The sine wave's frequency is slightly offset to create a subtle 'wobble' in the image. Increasing the slider increases the strength of the wobble.", "wobble", VERSION_HINT, 0.3, 0.0, 1.0),
new osci::EffectParameter("Wobble Phase", "Controls the phase of the wobble.", "wobblePhase", VERSION_HINT, 0.0, -1.0, 1.0), new osci::EffectParameter("Wobble Phase", "Controls the phase of the wobble.", "wobblePhase", VERSION_HINT, 0.0, -1.0, 1.0),
} });
); wobble->getParameter("wobblePhase")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);
wobble->getParameter("wobblePhase")->lfo->setUnnormalisedValueNotifyingHost((int) osci::LfoType::Sawtooth);
toggleableEffects.push_back(wobble); toggleableEffects.push_back(wobble);
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
delayEffect, delayEffect,
std::vector<osci::EffectParameter*>{ std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Delay Decay", "Adds repetitions, delays, or echos to the audio. This slider controls the volume of the echo.", "delayDecay", VERSION_HINT, 0.4, 0.0, 1.0), new osci::EffectParameter("Delay Decay", "Adds repetitions, delays, or echos to the audio. This slider controls the volume of the echo.", "delayDecay", VERSION_HINT, 0.4, 0.0, 1.0),
new osci::EffectParameter("Delay Length", "Controls the time in seconds between echos.", "delayLength", VERSION_HINT, 0.5, 0.0, 1.0) new osci::EffectParameter("Delay Length", "Controls the time in seconds between echos.", "delayLength", VERSION_HINT, 0.5, 0.0, 1.0)}));
}
));
toggleableEffects.push_back(std::make_shared<osci::Effect>( toggleableEffects.push_back(std::make_shared<osci::Effect>(
dashedLineEffect, dashedLineEffect,
std::vector<osci::EffectParameter*>{ std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Dash Length", "Controls the length of the dashed line.", "dashLength", VERSION_HINT, 0.2, 0.0, 1.0), new osci::EffectParameter("Dash Length", "Controls the length of the dashed line.", "dashLength", VERSION_HINT, 0.2, 0.0, 1.0),
} }));
));
toggleableEffects.push_back(custom); toggleableEffects.push_back(custom);
toggleableEffects.push_back(trace); toggleableEffects.push_back(trace);
trace->getParameter("traceLength")->lfo->setUnnormalisedValueNotifyingHost((int) osci::LfoType::Sawtooth); trace->getParameter("traceLength")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);
for (int i = 0; i < toggleableEffects.size(); i++) { for (int i = 0; i < toggleableEffects.size(); i++) {
auto effect = toggleableEffects[i]; auto effect = toggleableEffects[i];
@ -228,13 +219,12 @@ void OscirenderAudioProcessor::addLuaSlider() {
[this, sliderIndex](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this, sliderIndex](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
luaValues[sliderIndex].store(values[0]); luaValues[sliderIndex].store(values[0]);
return input; return input;
}, new osci::EffectParameter( },
new osci::EffectParameter(
"Lua Slider " + sliderName, "Lua Slider " + sliderName,
"Controls the value of the Lua variable called slider_" + sliderName.toLowerCase() + ".", "Controls the value of the Lua variable called slider_" + sliderName.toLowerCase() + ".",
"lua" + sliderName, "lua" + sliderName,
VERSION_HINT, 0.0, 0.0, 1.0 VERSION_HINT, 0.0, 0.0, 1.0)));
)
));
} }
void OscirenderAudioProcessor::addErrorListener(ErrorListener* listener) { void OscirenderAudioProcessor::addErrorListener(ErrorListener* listener) {
@ -258,10 +248,10 @@ void OscirenderAudioProcessor::updateEffectPrecedence() {
// parsersLock AND effectsLock must be locked before calling this function // parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessor::updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block) { void OscirenderAudioProcessor::updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block) {
if (index < 0 || index >= fileBlocks.size()) { if (index < 0 || index >= fileBlocks.size()) {
return; return;
} }
fileBlocks[index] = block; fileBlocks[index] = block;
openFile(index); openFile(index);
} }
// parsersLock AND effectsLock must be locked before calling this function // parsersLock AND effectsLock must be locked before calling this function
@ -269,7 +259,7 @@ void OscirenderAudioProcessor::addFile(juce::File file) {
fileBlocks.push_back(std::make_shared<juce::MemoryBlock>()); fileBlocks.push_back(std::make_shared<juce::MemoryBlock>());
fileNames.push_back(file.getFileName()); fileNames.push_back(file.getFileName());
fileIds.push_back(currentFileId++); fileIds.push_back(currentFileId++);
parsers.push_back(std::make_shared<FileParser>(*this, errorCallback)); parsers.push_back(std::make_shared<FileParser>(*this, errorCallback));
sounds.push_back(new ShapeSound(*this, parsers.back())); sounds.push_back(new ShapeSound(*this, parsers.back()));
file.createInputStream()->readIntoMemoryBlock(*fileBlocks.back()); file.createInputStream()->readIntoMemoryBlock(*fileBlocks.back());
@ -306,9 +296,9 @@ void OscirenderAudioProcessor::setFileRemovedCallback(std::function<void(int)> c
// parsersLock AND effectsLock must be locked before calling this function // parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessor::removeFile(int index) { void OscirenderAudioProcessor::removeFile(int index) {
if (index < 0 || index >= fileBlocks.size()) { if (index < 0 || index >= fileBlocks.size()) {
return; return;
} }
fileBlocks.erase(fileBlocks.begin() + index); fileBlocks.erase(fileBlocks.begin() + index);
fileNames.erase(fileNames.begin() + index); fileNames.erase(fileNames.begin() + index);
fileIds.erase(fileIds.begin() + index); fileIds.erase(fileIds.begin() + index);
@ -350,9 +340,9 @@ int OscirenderAudioProcessor::numFiles() {
// it will reparse any existing files, so it is safer. // it will reparse any existing files, so it is safer.
// parsersLock AND effectsLock must be locked before calling this function // parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessor::openFile(int index) { void OscirenderAudioProcessor::openFile(int index) {
if (index < 0 || index >= fileBlocks.size()) { if (index < 0 || index >= fileBlocks.size()) {
return; return;
} }
parsers[index]->parse(juce::String(fileIds[index]), fileNames[index], fileNames[index].fromLastOccurrenceOf(".", true, false).toLowerCase(), std::make_unique<juce::MemoryInputStream>(*fileBlocks[index], false), font); parsers[index]->parse(juce::String(fileIds[index]), fileNames[index], fileNames[index].fromLastOccurrenceOf(".", true, false).toLowerCase(), std::make_unique<juce::MemoryInputStream>(*fileBlocks[index], false), font);
changeCurrentFile(index); changeCurrentFile(index);
} }
@ -365,9 +355,9 @@ void OscirenderAudioProcessor::changeCurrentFile(int index) {
currentFile = -1; currentFile = -1;
changeSound(defaultSound); changeSound(defaultSound);
} }
if (index < 0 || index >= fileBlocks.size()) { if (index < 0 || index >= fileBlocks.size()) {
return; return;
} }
currentFile = index; currentFile = index;
changeSound(sounds[index]); changeSound(sounds[index]);
} }
@ -402,7 +392,7 @@ std::shared_ptr<FileParser> OscirenderAudioProcessor::getCurrentFileParser() {
juce::String OscirenderAudioProcessor::getCurrentFileName() { juce::String OscirenderAudioProcessor::getCurrentFileName() {
if (objectServerRendering || currentFile == -1) { if (objectServerRendering || currentFile == -1) {
return ""; return "";
} else { } else {
return fileNames[currentFile]; return fileNames[currentFile];
} }
@ -446,7 +436,7 @@ void OscirenderAudioProcessor::setObjectServerPort(int port) {
void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) { void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) {
juce::ScopedNoDenormals noDenormals; juce::ScopedNoDenormals noDenormals;
// Audio info variables // Audio info variables
int totalNumInputChannels = getTotalNumInputChannels(); int totalNumInputChannels = getTotalNumInputChannels();
int totalNumOutputChannels = getTotalNumOutputChannels(); int totalNumOutputChannels = getTotalNumOutputChannels();
double sampleRate = getSampleRate(); double sampleRate = getSampleRate();
@ -514,6 +504,7 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
outputBuffer3d.clear(); outputBuffer3d.clear();
{ {
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
juce::SpinLock::ScopedLockType sLock(syphonLock); juce::SpinLock::ScopedLockType sLock(syphonLock);
if (isSyphonInputActive()) { if (isSyphonInputActive()) {
for (int sample = 0; sample < outputBuffer3d.getNumSamples(); sample++) { for (int sample = 0; sample < outputBuffer3d.getNumSamples(); sample++) {
@ -521,7 +512,9 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
outputBuffer3d.setSample(0, sample, point.x); outputBuffer3d.setSample(0, sample, point.x);
outputBuffer3d.setSample(1, sample, point.y); outputBuffer3d.setSample(1, sample, point.y);
} }
} else if (usingInput && totalNumInputChannels >= 1) { } else
#endif
if (usingInput && totalNumInputChannels >= 1) {
if (totalNumInputChannels >= 2) { if (totalNumInputChannels >= 2) {
for (auto channel = 0; channel < juce::jmin(2, totalNumInputChannels); channel++) { for (auto channel = 0; channel < juce::jmin(2, totalNumInputChannels); channel++) {
outputBuffer3d.copyFrom(channel, 0, inputBuffer, channel, 0, buffer.getNumSamples()); outputBuffer3d.copyFrom(channel, 0, inputBuffer, channel, 0, buffer.getNumSamples());
@ -535,9 +528,10 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
// handle all midi messages // handle all midi messages
auto midiIterator = midiMessages.cbegin(); auto midiIterator = midiMessages.cbegin();
std::for_each(midiIterator, std::for_each(midiIterator,
midiMessages.cend(), midiMessages.cend(),
[&] (const juce::MidiMessageMetadata& meta) { synth.publicHandleMidiEvent(meta.getMessage()); } [&](const juce::MidiMessageMetadata& meta) {
); synth.publicHandleMidiEvent(meta.getMessage());
});
} else { } else {
juce::SpinLock::ScopedLockType lock1(parsersLock); juce::SpinLock::ScopedLockType lock1(parsersLock);
juce::SpinLock::ScopedLockType lock2(effectsLock); juce::SpinLock::ScopedLockType lock2(effectsLock);
@ -556,7 +550,7 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
auto* channelData = buffer.getArrayOfWritePointers(); auto* channelData = buffer.getArrayOfWritePointers();
for (int sample = 0; sample < buffer.getNumSamples(); ++sample) { for (int sample = 0; sample < buffer.getNumSamples(); ++sample) {
if (animateFrames->getBoolValue()) { if (animateFrames->getBoolValue()) {
if (juce::JUCEApplicationBase::isStandaloneApp()) { if (juce::JUCEApplicationBase::isStandaloneApp()) {
animationFrame = animationFrame + sTimeSec * animationRate->getValueUnnormalised(); animationFrame = animationFrame + sTimeSec * animationRate->getValueUnnormalised();
@ -573,7 +567,7 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
if (loopAnimation->getBoolValue()) { if (loopAnimation->getBoolValue()) {
animationFrame = std::fmod(animationFrame, totalFrames); animationFrame = std::fmod(animationFrame, totalFrames);
} else { } else {
animationFrame = juce::jlimit(0.0, (double) totalFrames - 1, animationFrame.load()); animationFrame = juce::jlimit(0.0, (double)totalFrames - 1, animationFrame.load());
} }
sounds[currentFile]->parser->setFrame(animationFrame); sounds[currentFile]->parser->setFrame(animationFrame);
} }
@ -598,7 +592,7 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
currentVolume = std::sqrt(squaredVolume); currentVolume = std::sqrt(squaredVolume);
currentVolume = juce::jlimit(0.0, 1.0, currentVolume); currentVolume = juce::jlimit(0.0, 1.0, currentVolume);
osci::Point channels = { outputBuffer3d.getSample(0, sample), outputBuffer3d.getSample(1, sample), outputBuffer3d.getSample(2, sample) }; osci::Point channels = {outputBuffer3d.getSample(0, sample), outputBuffer3d.getSample(1, sample), outputBuffer3d.getSample(2, sample)};
{ {
juce::SpinLock::ScopedLockType lock1(parsersLock); juce::SpinLock::ScopedLockType lock1(parsersLock);
@ -621,8 +615,8 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
} }
} }
double x = channels.x; double x = channels.x;
double y = channels.y; double y = channels.y;
x *= volume; x *= volume;
y *= volume; y *= volume;
@ -640,9 +634,9 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
} }
if (totalNumOutputChannels >= 2) { if (totalNumOutputChannels >= 2) {
channelData[0][sample] = x; channelData[0][sample] = x;
channelData[1][sample] = y; channelData[1][sample] = y;
} else if (totalNumOutputChannels == 1) { } else if (totalNumOutputChannels == 1) {
channelData[0][sample] = x; channelData[0][sample] = x;
} }
@ -650,7 +644,7 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
playTimeSeconds += sTimeSec; playTimeSeconds += sTimeSec;
playTimeBeats += sTimeBeats; playTimeBeats += sTimeBeats;
} }
} }
// used for any callback that must guarantee all audio is recieved (e.g. when recording to a file) // used for any callback that must guarantee all audio is recieved (e.g. when recording to a file)
juce::SpinLock::ScopedLockType lock(audioThreadCallbackLock); juce::SpinLock::ScopedLockType lock(audioThreadCallbackLock);
@ -904,7 +898,7 @@ void OscirenderAudioProcessor::envelopeChanged(EnvelopeComponent* changedEnvelop
} }
} }
#if JUCE_MAC || JUCE_WINDOWS #if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
// Syphon/Spout input management // Syphon/Spout input management
// syphonLock must be held when calling this function // syphonLock must be held when calling this function

Wyświetl plik

@ -11,40 +11,43 @@
#define VERSION_HINT 2 #define VERSION_HINT 2
#include <JuceHeader.h> #include <JuceHeader.h>
#include "audio/ShapeSound.h"
#include "audio/ShapeVoice.h"
#include "audio/PublicSynthesiser.h"
#include "audio/SampleRateManager.h"
#include <numbers> #include <numbers>
#include "audio/DelayEffect.h"
#include "audio/WobbleEffect.h" #include "CommonPluginProcessor.h"
#include "audio/PerspectiveEffect.h"
#include "obj/ObjectServer.h"
#include "UGen/Env.h" #include "UGen/Env.h"
#include "UGen/ugen_JuceEnvelopeComponent.h" #include "UGen/ugen_JuceEnvelopeComponent.h"
#include "audio/CustomEffect.h" #include "audio/CustomEffect.h"
#include "audio/DashedLineEffect.h" #include "audio/DashedLineEffect.h"
#include "CommonPluginProcessor.h" #include "audio/DelayEffect.h"
#include "SyphonFrameGrabber.h" #include "audio/PerspectiveEffect.h"
#include "audio/PublicSynthesiser.h"
#include "audio/SampleRateManager.h"
#include "audio/ShapeSound.h"
#include "audio/ShapeVoice.h"
#include "audio/WobbleEffect.h"
#include "obj/ObjectServer.h"
#if JUCE_MAC || JUCE_WINDOWS #if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
#include "../modules/juce_sharedtexture/SharedTexture.h" #include "../modules/juce_sharedtexture/SharedTexture.h"
#include "video/SyphonFrameGrabber.h"
#endif #endif
//============================================================================== //==============================================================================
/** /**
*/ */
class OscirenderAudioProcessor : public CommonAudioProcessor, juce::AudioProcessorParameter::Listener, public EnvelopeComponentListener class OscirenderAudioProcessor : public CommonAudioProcessor, juce::AudioProcessorParameter::Listener, public EnvelopeComponentListener
#if JucePlugin_Enable_ARA #if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension ,
#endif public juce::AudioProcessorARAExtension
#endif
{ {
public: public:
OscirenderAudioProcessor(); OscirenderAudioProcessor();
~OscirenderAudioProcessor() override; ~OscirenderAudioProcessor() override;
void prepareToPlay (double sampleRate, int samplesPerBlock) override; void prepareToPlay(double sampleRate, int samplesPerBlock) override;
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override; void processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
juce::AudioProcessorEditor* createEditor() override; juce::AudioProcessorEditor* createEditor() override;
@ -56,21 +59,20 @@ public:
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override; void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
void envelopeChanged(EnvelopeComponent* changedEnvelope) override; void envelopeChanged(EnvelopeComponent* changedEnvelope) override;
std::vector<std::shared_ptr<osci::Effect>> toggleableEffects; std::vector<std::shared_ptr<osci::Effect>> toggleableEffects;
std::vector<std::shared_ptr<osci::Effect>> luaEffects; std::vector<std::shared_ptr<osci::Effect>> luaEffects;
std::atomic<double> luaValues[26] = { 0.0 }; std::atomic<double> luaValues[26] = {0.0};
std::shared_ptr<osci::Effect> frequencyEffect = std::make_shared<osci::Effect>( std::shared_ptr<osci::Effect> frequencyEffect = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
frequency = values[0].load(); frequency = values[0].load();
return input; return input;
}, new osci::EffectParameter( },
new osci::EffectParameter(
"Frequency", "Frequency",
"Controls how many times per second the image is drawn, thereby controlling the pitch of the sound. Lower frequencies result in more-accurately drawn images, but more flickering, and vice versa.", "Controls how many times per second the image is drawn, thereby controlling the pitch of the sound. Lower frequencies result in more-accurately drawn images, but more flickering, and vice versa.",
"frequency", "frequency",
VERSION_HINT, 220.0, 0.0, 4200.0 VERSION_HINT, 220.0, 0.0, 4200.0));
)
);
std::shared_ptr<osci::Effect> trace = std::make_shared<osci::Effect>( std::shared_ptr<osci::Effect> trace = std::make_shared<osci::Effect>(
std::vector<osci::EffectParameter*>{ std::vector<osci::EffectParameter*>{
@ -78,16 +80,13 @@ public:
"Trace Start", "Trace Start",
"Defines how far into the frame the drawing is started at. This has the effect of 'tracing' out the image from a single dot when animated. By default, we start drawing from the beginning of the frame, so this value is 0.0.", "Defines how far into the frame the drawing is started at. This has the effect of 'tracing' out the image from a single dot when animated. By default, we start drawing from the beginning of the frame, so this value is 0.0.",
"traceStart", "traceStart",
VERSION_HINT, 0.0, 0.0, 1.0, 0.001 VERSION_HINT, 0.0, 0.0, 1.0, 0.001),
),
new osci::EffectParameter( new osci::EffectParameter(
"Trace Length", "Trace Length",
"Defines how much of the frame is drawn per cycle. This has the effect of 'tracing' out the image from a single dot when animated. By default, we draw the whole frame, corresponding to a value of 1.0.", "Defines how much of the frame is drawn per cycle. This has the effect of 'tracing' out the image from a single dot when animated. By default, we draw the whole frame, corresponding to a value of 1.0.",
"traceLength", "traceLength",
VERSION_HINT, 1.0, 0.0, 1.0, 0.001 VERSION_HINT, 1.0, 0.0, 1.0, 0.001),
), });
}
);
std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>(); std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>();
@ -97,8 +96,7 @@ public:
std::shared_ptr<CustomEffect> customEffect = std::make_shared<CustomEffect>(errorCallback, luaValues); std::shared_ptr<CustomEffect> customEffect = std::make_shared<CustomEffect>(errorCallback, luaValues);
std::shared_ptr<osci::Effect> custom = std::make_shared<osci::Effect>( std::shared_ptr<osci::Effect> custom = std::make_shared<osci::Effect>(
customEffect, customEffect,
new osci::EffectParameter("Lua Effect", "Controls the strength of the custom Lua effect applied. You can write your own custom effect using Lua by pressing the edit button on the right.", "customEffectStrength", VERSION_HINT, 1.0, 0.0, 1.0) new osci::EffectParameter("Lua Effect", "Controls the strength of the custom Lua effect applied. You can write your own custom effect using Lua by pressing the edit button on the right.", "customEffectStrength", VERSION_HINT, 1.0, 0.0, 1.0));
);
std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>(); std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>();
std::shared_ptr<osci::Effect> perspective = std::make_shared<osci::Effect>( std::shared_ptr<osci::Effect> perspective = std::make_shared<osci::Effect>(
@ -106,8 +104,7 @@ public:
std::vector<osci::EffectParameter*>{ std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Perspective", "Controls the strength of the 3D perspective projection.", "perspectiveStrength", VERSION_HINT, 1.0, 0.0, 1.0), new osci::EffectParameter("Perspective", "Controls the strength of the 3D perspective projection.", "perspectiveStrength", VERSION_HINT, 1.0, 0.0, 1.0),
new osci::EffectParameter("Focal Length", "Controls the focal length of the 3D perspective effect. A higher focal length makes the image look more flat, and a lower focal length makes the image look more 3D.", "perspectiveFocalLength", VERSION_HINT, 2.0, 0.0, 10.0), new osci::EffectParameter("Focal Length", "Controls the focal length of the 3D perspective effect. A higher focal length makes the image look more flat, and a lower focal length makes the image look more 3D.", "perspectiveFocalLength", VERSION_HINT, 2.0, 0.0, 10.0),
} });
);
osci::BooleanParameter* midiEnabled = new osci::BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false, "Enable MIDI input for the synth. If disabled, the synth will play a constant tone, as controlled by the frequency slider."); osci::BooleanParameter* midiEnabled = new osci::BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false, "Enable MIDI input for the synth. If disabled, the synth will play a constant tone, as controlled by the frequency slider.");
osci::BooleanParameter* inputEnabled = new osci::BooleanParameter("Audio Input Enabled", "inputEnabled", VERSION_HINT, false, "Enable to use input audio, instead of the generated audio."); osci::BooleanParameter* inputEnabled = new osci::BooleanParameter("Audio Input Enabled", "inputEnabled", VERSION_HINT, false, "Enable to use input audio, instead of the generated audio.");
@ -133,7 +130,7 @@ public:
osci::FloatParameter* releaseTime = new osci::FloatParameter("Release Time", "releaseTime", VERSION_HINT, 0.4, 0.0, 1.0); osci::FloatParameter* releaseTime = new osci::FloatParameter("Release Time", "releaseTime", VERSION_HINT, 0.4, 0.0, 1.0);
osci::FloatParameter* attackShape = new osci::FloatParameter("Attack Shape", "attackShape", VERSION_HINT, 5, -50, 50); osci::FloatParameter* attackShape = new osci::FloatParameter("Attack Shape", "attackShape", VERSION_HINT, 5, -50, 50);
osci::FloatParameter* decayShape = new osci::FloatParameter("Decay osci::Shape", "decayShape", VERSION_HINT, -20, -50, 50); osci::FloatParameter* decayShape = new osci::FloatParameter("Decay osci::Shape", "decayShape", VERSION_HINT, -20, -50, 50);
osci::FloatParameter* releaseShape = new osci::FloatParameter("Release Shape", "releaseShape", VERSION_HINT, -5,-50, 50); osci::FloatParameter* releaseShape = new osci::FloatParameter("Release Shape", "releaseShape", VERSION_HINT, -5, -50, 50);
Env adsrEnv = Env::adsr( Env adsrEnv = Env::adsr(
attackTime->getValueUnnormalised(), attackTime->getValueUnnormalised(),
@ -141,8 +138,7 @@ public:
sustainLevel->getValueUnnormalised(), sustainLevel->getValueUnnormalised(),
releaseTime->getValueUnnormalised(), releaseTime->getValueUnnormalised(),
1.0, 1.0,
std::vector<EnvCurve>{ attackShape->getValueUnnormalised(), decayShape->getValueUnnormalised(), releaseShape->getValueUnnormalised() } std::vector<EnvCurve>{attackShape->getValueUnnormalised(), decayShape->getValueUnnormalised(), releaseShape->getValueUnnormalised()});
);
juce::MidiKeyboardState keyboardState; juce::MidiKeyboardState keyboardState;
@ -158,23 +154,21 @@ public:
std::shared_ptr<osci::Effect> imageThreshold = std::make_shared<osci::Effect>( std::shared_ptr<osci::Effect> imageThreshold = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
return input; return input;
}, new osci::EffectParameter( },
new osci::EffectParameter(
"Image Threshold", "Image Threshold",
"Controls the probability of visiting a dark pixel versus a light pixel. Darker pixels are less likely to be visited, so turning the threshold to a lower value makes it more likely to visit dark pixels.", "Controls the probability of visiting a dark pixel versus a light pixel. Darker pixels are less likely to be visited, so turning the threshold to a lower value makes it more likely to visit dark pixels.",
"imageThreshold", "imageThreshold",
VERSION_HINT, 0.5, 0, 1 VERSION_HINT, 0.5, 0, 1));
)
);
std::shared_ptr<osci::Effect> imageStride = std::make_shared<osci::Effect>( std::shared_ptr<osci::Effect> imageStride = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) { [this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
return input; return input;
}, new osci::EffectParameter( },
new osci::EffectParameter(
"Image Stride", "Image Stride",
"Controls the spacing between pixels when drawing an image. Larger values mean more of the image can be drawn, but at a lower fidelity.", "Controls the spacing between pixels when drawing an image. Larger values mean more of the image can be drawn, but at a lower fidelity.",
"imageStride", "imageStride",
VERSION_HINT, 4, 1, 50, 1 VERSION_HINT, 4, 1, 50, 1));
)
);
std::atomic<double> animationFrame = 0.f; std::atomic<double> animationFrame = 0.f;
@ -202,10 +196,10 @@ public:
void openFile(int index); void openFile(int index);
int getCurrentFileIndex(); int getCurrentFileIndex();
std::shared_ptr<FileParser> getCurrentFileParser(); std::shared_ptr<FileParser> getCurrentFileParser();
juce::String getCurrentFileName(); juce::String getCurrentFileName();
juce::String getFileName(int index); juce::String getFileName(int index);
juce::String getFileId(int index); juce::String getFileId(int index);
std::shared_ptr<juce::MemoryBlock> getFileBlock(int index); std::shared_ptr<juce::MemoryBlock> getFileBlock(int index);
void setObjectServerRendering(bool enabled); void setObjectServerRendering(bool enabled);
void setObjectServerPort(int port); void setObjectServerPort(int port);
void addErrorListener(ErrorListener* listener); void addErrorListener(ErrorListener* listener);
@ -240,7 +234,6 @@ public:
}; };
private: private:
std::atomic<bool> prevMidiEnabled = !midiEnabled->getBoolValue(); std::atomic<bool> prevMidiEnabled = !midiEnabled->getBoolValue();
juce::SpinLock audioThreadCallbackLock; juce::SpinLock audioThreadCallbackLock;
@ -271,7 +264,7 @@ private:
std::istringstream parser(input.toStdString()); std::istringstream parser(input.toStdString());
parser >> result[0]; parser >> result[0];
for (int idx = 1; idx < 3; idx++) { for (int idx = 1; idx < 3; idx++) {
parser.get(); //Skip period parser.get(); // Skip period
parser >> result[idx]; parser >> result[idx];
} }
} }
@ -287,8 +280,7 @@ private:
juce::AudioPlayHead* playHead; juce::AudioPlayHead* playHead;
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
#if JUCE_MAC || JUCE_WINDOWS
public: public:
bool isSyphonInputActive() const; bool isSyphonInputActive() const;
bool isSyphonInputStarted() const; bool isSyphonInputStarted() const;
@ -297,11 +289,12 @@ public:
juce::String getSyphonSourceName() const; juce::String getSyphonSourceName() const;
juce::SpinLock syphonLock; juce::SpinLock syphonLock;
private: private:
ImageParser syphonImageParser = ImageParser(*this); ImageParser syphonImageParser = ImageParser(*this);
std::unique_ptr<SyphonFrameGrabber> syphonFrameGrabber; std::unique_ptr<SyphonFrameGrabber> syphonFrameGrabber;
#endif #endif
//============================================================================== //==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessor) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OscirenderAudioProcessor)
}; };

Wyświetl plik

@ -1,4 +1,5 @@
#include "SettingsComponent.h" #include "SettingsComponent.h"
#include "PluginEditor.h" #include "PluginEditor.h"
SettingsComponent::SettingsComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor) { SettingsComponent::SettingsComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor) {
@ -23,7 +24,6 @@ SettingsComponent::SettingsComponent(OscirenderAudioProcessor& p, OscirenderAudi
mainLayout.setItemLayout(2, -0.1, -0.9, -(1.0 + mainLayoutPreferredSize)); mainLayout.setItemLayout(2, -0.1, -0.9, -(1.0 + mainLayoutPreferredSize));
} }
void SettingsComponent::resized() { void SettingsComponent::resized() {
auto area = getLocalBounds(); auto area = getLocalBounds();
area.removeFromLeft(5); area.removeFromLeft(5);
@ -38,11 +38,11 @@ void SettingsComponent::resized() {
juce::Component dummy; juce::Component dummy;
juce::Component dummy2; juce::Component dummy2;
juce::Component* midiComponents[] = { &dummy, &midiResizerBar, &midi }; juce::Component* midiComponents[] = {&dummy, &midiResizerBar, &midi};
midiLayout.layOutComponents(midiComponents, 3, area.getX(), area.getY(), area.getWidth(), area.getHeight(), true, true); midiLayout.layOutComponents(midiComponents, 3, area.getX(), area.getY(), area.getWidth(), area.getHeight(), true, true);
midi.setBounds(midi.getBounds()); midi.setBounds(midi.getBounds());
juce::Component* columns[] = { &dummy2, &mainResizerBar, &dummy }; juce::Component* columns[] = {&dummy2, &mainResizerBar, &dummy};
mainLayout.layOutComponents(columns, 3, dummy.getX(), dummy.getY(), dummy.getWidth(), dummy.getHeight(), false, true); mainLayout.layOutComponents(columns, 3, dummy.getX(), dummy.getY(), dummy.getWidth(), dummy.getHeight(), false, true);
auto bounds = dummy2.getBounds(); auto bounds = dummy2.getBounds();
@ -79,8 +79,25 @@ void SettingsComponent::fileUpdated(juce::String fileName) {
juce::String extension = fileName.fromLastOccurrenceOf(".", true, false).toLowerCase(); juce::String extension = fileName.fromLastOccurrenceOf(".", true, false).toLowerCase();
txt.setVisible(false); txt.setVisible(false);
frame.setVisible(false); frame.setVisible(false);
bool isImage = extension == ".gif" || extension == ".png" || extension == ".jpg" || extension == ".jpeg" || extension == ".mov" || extension == ".mp4" || audioProcessor.isSyphonInputStarted();
if ((fileName.isEmpty() && !audioProcessor.isSyphonInputStarted()) || audioProcessor.objectServerRendering) { // Check if the file is an image based on extension or Syphon/Spout input
bool isSyphonActive = false;
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
isSyphonActive = audioProcessor.isSyphonInputStarted();
#endif
bool isImage = isSyphonActive ||
(extension == ".gif" ||
extension == ".png" ||
extension == ".jpg" ||
extension == ".jpeg" ||
extension == ".mov" ||
extension == ".mp4");
// Skip processing if object server is rendering or if no file is selected and no Syphon input
bool skipProcessing = audioProcessor.objectServerRendering || (fileName.isEmpty() && !isSyphonActive);
if (skipProcessing) {
// do nothing // do nothing
} else if (extension == ".txt") { } else if (extension == ".txt") {
txt.setVisible(true); txt.setVisible(true);

Wyświetl plik

@ -62,6 +62,7 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
editor.openRecordingSettings(); editor.openRecordingSettings();
}); });
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
// Add Syphon/Spout input menu item under Recording // Add Syphon/Spout input menu item under Recording
addMenuItem(2, audioProcessor.isSyphonInputActive() ? "Disconnect Syphon/Spout Input" : "Select Syphon/Spout Input...", [this] { addMenuItem(2, audioProcessor.isSyphonInputActive() ? "Disconnect Syphon/Spout Input" : "Select Syphon/Spout Input...", [this] {
if (audioProcessor.isSyphonInputActive()) if (audioProcessor.isSyphonInputActive())
@ -69,6 +70,7 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
else else
openSyphonInputDialog(); openSyphonInputDialog();
}); });
#endif
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) { if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
addMenuItem(3, "Settings...", [this] { addMenuItem(3, "Settings...", [this] {
@ -77,6 +79,7 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
} }
} }
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
void OsciMainMenuBarModel::openSyphonInputDialog() { void OsciMainMenuBarModel::openSyphonInputDialog() {
editor.openSyphonInputDialog(); editor.openSyphonInputDialog();
} }
@ -84,3 +87,4 @@ void OsciMainMenuBarModel::openSyphonInputDialog() {
void OsciMainMenuBarModel::disconnectSyphonInput() { void OsciMainMenuBarModel::disconnectSyphonInput() {
audioProcessor.disconnectSyphonInput(); audioProcessor.disconnectSyphonInput();
} }
#endif

Wyświetl plik

@ -0,0 +1,343 @@
#include "FFmpegEncoderManager.h"
FFmpegEncoderManager::FFmpegEncoderManager(juce::File& ffmpegExecutable)
: ffmpegExecutable(ffmpegExecutable) {
queryAvailableEncoders();
}
juce::String FFmpegEncoderManager::buildVideoEncodingCommand(
VideoCodec codec,
int crf,
int videoToolboxQuality,
int width,
int height,
double frameRate,
const juce::String& compressionPreset,
const juce::File& outputFile) {
switch (codec) {
case VideoCodec::H264:
return buildH264EncodingCommand(crf, width, height, frameRate, compressionPreset, outputFile);
case VideoCodec::H265:
return buildH265EncodingCommand(crf, videoToolboxQuality, width, height, frameRate, compressionPreset, outputFile);
case VideoCodec::VP9:
return buildVP9EncodingCommand(crf, width, height, frameRate, compressionPreset, outputFile);
#if JUCE_MAC
case VideoCodec::ProRes:
return buildProResEncodingCommand(width, height, frameRate, outputFile);
#endif
default:
// Default to H.264 if unknown codec
return buildH264EncodingCommand(crf, width, height, frameRate, compressionPreset, outputFile);
}
}
juce::Array<FFmpegEncoderManager::EncoderDetails> FFmpegEncoderManager::getAvailableEncodersForCodec(VideoCodec codec) {
// Return cached list of encoders if available
auto it = availableEncoders.find(codec);
if (it != availableEncoders.end()) {
return it->second;
}
return {};
}
bool FFmpegEncoderManager::isHardwareEncoderAvailable(const juce::String& encoderName) {
// Check if the encoder is available and supported
for (auto& pair : availableEncoders) {
for (auto& encoder : pair.second) {
if (encoder.name == encoderName && encoder.isSupported && encoder.isHardwareAccelerated) {
return true;
}
}
}
return false;
}
juce::String FFmpegEncoderManager::getBestEncoderForCodec(VideoCodec codec) {
auto encoders = getAvailableEncodersForCodec(codec);
// Define priority lists for each codec type
juce::StringArray h264Encoders = {"h264_nvenc", "h264_amf", "h264_qsv", "h264_videotoolbox", "libx264"};
juce::StringArray h265Encoders = {"hevc_nvenc", "hevc_amf", "hevc_qsv", "hevc_videotoolbox", "libx265"};
juce::StringArray vp9Encoders = {"libvpx-vp9"};
#if JUCE_MAC
juce::StringArray proResEncoders = {"prores_ks", "prores"};
#endif
// Select the appropriate priority list based on codec
juce::StringArray* priorityList = nullptr;
switch (codec) {
case VideoCodec::H264:
priorityList = &h264Encoders;
break;
case VideoCodec::H265:
priorityList = &h265Encoders;
break;
case VideoCodec::VP9:
priorityList = &vp9Encoders;
break;
#if JUCE_MAC
case VideoCodec::ProRes:
priorityList = &proResEncoders;
break;
#endif
default:
priorityList = &h264Encoders; // Default to H.264
}
// Find the highest priority encoder that is available
for (const auto& encoderName : *priorityList) {
for (const auto& encoder : encoders) {
if (encoder.name == encoderName && encoder.isSupported) {
return encoderName;
}
}
}
// Return default software encoder if no hardware encoder is available
switch (codec) {
case VideoCodec::H264:
return "libx264";
case VideoCodec::H265:
return "libx265";
case VideoCodec::VP9:
return "libvpx-vp9";
#if JUCE_MAC
case VideoCodec::ProRes:
return "prores";
#endif
default:
return "libx264";
}
}
void FFmpegEncoderManager::queryAvailableEncoders() {
// Query available encoders using ffmpeg -encoders
juce::String output = runFFmpegCommand({"-encoders", "-hide_banner"});
parseEncoderList(output);
}
void FFmpegEncoderManager::parseEncoderList(const juce::String& output) {
// Clear current encoders
availableEncoders.clear();
// Initialize codec-specific encoder arrays
availableEncoders[VideoCodec::H264] = {};
availableEncoders[VideoCodec::H265] = {};
availableEncoders[VideoCodec::VP9] = {};
#if JUCE_MAC
availableEncoders[VideoCodec::ProRes] = {};
#endif
// Split the output into lines
juce::StringArray lines;
lines.addLines(output);
// Skip the first 10 lines (header information from ffmpeg -encoders)
int linesToSkip = juce::jmin(10, lines.size());
// Parse each line to find encoder information
for (int i = linesToSkip; i < lines.size(); ++i) {
const auto& line = lines[i];
// Format: V..... libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
juce::String flags = line.substring(0, 6).trim();
juce::String name = line.substring(8).upToFirstOccurrenceOf(" ", false, true);
juce::String description = line.substring(8 + name.length()).trim();
EncoderDetails encoder;
encoder.name = name;
encoder.description = description;
encoder.isHardwareAccelerated = name.contains("nvenc") || name.contains("amf") ||
name.contains("qsv") || name.contains("videotoolbox");
encoder.isSupported = flags.contains("V"); // Video encoder
// Add encoder to appropriate codec list
if (name == "libx264" || name.startsWith("h264_")) {
availableEncoders[VideoCodec::H264].add(encoder);
} else if (name == "libx265" || name.startsWith("hevc_")) {
availableEncoders[VideoCodec::H265].add(encoder);
} else if (name == "libvpx-vp9") {
availableEncoders[VideoCodec::VP9].add(encoder);
}
#if JUCE_MAC
else if (name.startsWith("prores")) {
availableEncoders[VideoCodec::ProRes].add(encoder);
}
#endif
}
}
juce::String FFmpegEncoderManager::runFFmpegCommand(const juce::StringArray& args) {
juce::ChildProcess process;
juce::StringArray command;
command.add(ffmpegExecutable.getFullPathName());
command.addArray(args);
process.start(command, juce::ChildProcess::wantStdOut);
juce::String output = process.readAllProcessOutput();
return output;
}
juce::String FFmpegEncoderManager::buildBaseEncodingCommand(
int width,
int height,
double frameRate,
const juce::File& outputFile) {
juce::String resolution = juce::String(width) + "x" + juce::String(height);
juce::String cmd = "\"" + ffmpegExecutable.getFullPathName() + "\"" +
" -r " + juce::String(frameRate) +
" -f rawvideo" +
" -pix_fmt rgba" +
" -s " + resolution +
" -i -" +
" -threads 4" +
" -y" +
" -pix_fmt yuv420p" +
" -vf vflip";
return cmd;
}
juce::String FFmpegEncoderManager::addH264EncoderSettings(
juce::String cmd,
const juce::String& encoderName,
int crf,
const juce::String& compressionPreset) {
if (encoderName == "h264_nvenc") {
cmd += " -c:v h264_nvenc";
cmd += " -preset p7";
cmd += " -profile:v high";
cmd += " -rc vbr";
cmd += " -cq " + juce::String(crf);
cmd += " -b:v 0";
} else if (encoderName == "h264_amf") {
cmd += " -c:v h264_amf";
cmd += " -quality quality";
cmd += " -rc cqp";
cmd += " -qp_i " + juce::String(crf);
cmd += " -qp_p " + juce::String(crf);
} else if (encoderName == "h264_qsv") {
cmd += " -c:v h264_qsv";
cmd += " -global_quality " + juce::String(crf);
cmd += " -preset " + compressionPreset;
} else if (encoderName == "h264_videotoolbox") {
cmd += " -c:v h264_videotoolbox";
cmd += " -q " + juce::String(crf);
} else { // libx264 (software)
cmd += " -c:v libx264";
cmd += " -preset " + compressionPreset;
cmd += " -crf " + juce::String(crf);
}
return cmd;
}
juce::String FFmpegEncoderManager::addH265EncoderSettings(
juce::String cmd,
const juce::String& encoderName,
int crf,
int videoToolboxQuality,
const juce::String& compressionPreset) {
if (encoderName == "hevc_nvenc") {
cmd += " -c:v hevc_nvenc";
cmd += " -preset p7";
cmd += " -profile:v main";
cmd += " -rc vbr";
cmd += " -cq " + juce::String(crf);
cmd += " -b:v 0";
} else if (encoderName == "hevc_amf") {
cmd += " -c:v hevc_amf";
cmd += " -quality quality";
cmd += " -rc cqp";
cmd += " -qp_i " + juce::String(crf);
cmd += " -qp_p " + juce::String(crf);
} else if (encoderName == "hevc_qsv") {
cmd += " -c:v hevc_qsv";
cmd += " -global_quality " + juce::String(crf);
cmd += " -preset " + compressionPreset;
} else if (encoderName == "hevc_videotoolbox") {
cmd += " -c:v hevc_videotoolbox";
cmd += " -q:v " + juce::String(videoToolboxQuality);
cmd += " -tag:v hvc1";
} else { // libx265 (software)
cmd += " -c:v libx265";
cmd += " -preset " + compressionPreset;
cmd += " -crf " + juce::String(crf);
}
return cmd;
}
juce::String FFmpegEncoderManager::buildH264EncodingCommand(
int crf,
int width,
int height,
double frameRate,
const juce::String& compressionPreset,
const juce::File& outputFile) {
juce::String cmd = buildBaseEncodingCommand(width, height, frameRate, outputFile);
juce::String bestEncoder = getBestEncoderForCodec(VideoCodec::H264);
cmd = addH264EncoderSettings(cmd, bestEncoder, crf, compressionPreset);
cmd += " \"" + outputFile.getFullPathName() + "\"";
return cmd;
}
juce::String FFmpegEncoderManager::buildH265EncodingCommand(
int crf,
int videoToolboxQuality,
int width,
int height,
double frameRate,
const juce::String& compressionPreset,
const juce::File& outputFile) {
juce::String cmd = buildBaseEncodingCommand(width, height, frameRate, outputFile);
juce::String bestEncoder = getBestEncoderForCodec(VideoCodec::H265);
cmd = addH265EncoderSettings(cmd, bestEncoder, crf, videoToolboxQuality, compressionPreset);
cmd += " \"" + outputFile.getFullPathName() + "\"";
return cmd;
}
juce::String FFmpegEncoderManager::buildVP9EncodingCommand(
int crf,
int width,
int height,
double frameRate,
const juce::String& compressionPreset,
const juce::File& outputFile) {
juce::String cmd = buildBaseEncodingCommand(width, height, frameRate, outputFile);
cmd += juce::String(" -c:v libvpx-vp9") +
" -b:v 0" +
" -crf " + juce::String(crf) +
" -deadline good -cpu-used 2";
cmd += " \"" + outputFile.getFullPathName() + "\"";
return cmd;
}
#if JUCE_MAC
juce::String FFmpegEncoderManager::buildProResEncodingCommand(
int width,
int height,
double frameRate,
const juce::File& outputFile) {
juce::String cmd = buildBaseEncodingCommand(width, height, frameRate, outputFile);
juce::String bestEncoder = getBestEncoderForCodec(VideoCodec::ProRes);
cmd += " -c:v " + bestEncoder +
" -profile:v 3"; // ProRes 422 HQ
cmd += " \"" + outputFile.getFullPathName() + "\"";
return cmd;
}
#endif

Wyświetl plik

@ -0,0 +1,112 @@
#pragma once
#include <JuceHeader.h>
#include "../visualiser/RecordingSettings.h"
class FFmpegEncoderManager {
public:
FFmpegEncoderManager(juce::File& ffmpegExecutable);
~FFmpegEncoderManager() = default;
struct EncoderDetails {
juce::String name;
juce::String description;
bool isHardwareAccelerated;
bool isSupported;
};
// FFMPEG command builder
juce::String buildVideoEncodingCommand(
VideoCodec codec,
int crf,
int videoToolboxQuality,
int width,
int height,
double frameRate,
const juce::String& compressionPreset,
const juce::File& outputFile);
// Get available encoders for a given codec
juce::Array<EncoderDetails> getAvailableEncodersForCodec(VideoCodec codec);
// Check if a hardware encoder is available
bool isHardwareEncoderAvailable(const juce::String& encoderName);
// Get the best encoder for a given codec
juce::String getBestEncoderForCodec(VideoCodec codec);
private:
juce::File ffmpegExecutable;
std::map<VideoCodec, juce::Array<EncoderDetails>> availableEncoders;
// Query available encoders from FFmpeg
void queryAvailableEncoders();
// Parse encoder output from FFmpeg
void parseEncoderList(const juce::String& output);
// Run FFmpeg with given arguments and return output
juce::String runFFmpegCommand(const juce::StringArray& args);
// Common base command builder to reduce duplication
juce::String buildBaseEncodingCommand(
int width,
int height,
double frameRate,
const juce::File& outputFile);
// H.264 encoder settings helper
juce::String addH264EncoderSettings(
juce::String cmd,
const juce::String& encoderName,
int crf,
const juce::String& compressionPreset);
// H.265 encoder settings helper
juce::String addH265EncoderSettings(
juce::String cmd,
const juce::String& encoderName,
int crf,
int videoToolboxQuality,
const juce::String& compressionPreset);
// Build H.264 encoding command
juce::String buildH264EncodingCommand(
int crf,
int width,
int height,
double frameRate,
const juce::String& compressionPreset,
const juce::File& outputFile);
// Build H.265 encoding command
juce::String buildH265EncodingCommand(
int crf,
int videoToolboxQuality,
int width,
int height,
double frameRate,
const juce::String& compressionPreset,
const juce::File& outputFile);
// Build VP9 encoding command
juce::String buildVP9EncodingCommand(
int crf,
int width,
int height,
double frameRate,
const juce::String& compressionPreset,
const juce::File& outputFile);
#if JUCE_MAC
// Build ProRes encoding command
juce::String buildProResEncodingCommand(
int width,
int height,
double frameRate,
const juce::File& outputFile);
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FFmpegEncoderManager)
};

Wyświetl plik

@ -1,13 +1,12 @@
#pragma once #pragma once
#include <JuceHeader.h> #include <JuceHeader.h>
#include "InvisibleOpenGLContextComponent.h" #include "InvisibleOpenGLContextComponent.h"
class SyphonFrameGrabber : private juce::Thread, public juce::Component class SyphonFrameGrabber : private juce::Thread, public juce::Component {
{
public: public:
SyphonFrameGrabber(SharedTextureManager& manager, juce::String server, juce::String app, ImageParser& parser, int pollMs = 16) SyphonFrameGrabber(SharedTextureManager& manager, juce::String server, juce::String app, ImageParser& parser, int pollMs = 16)
: juce::Thread("SyphonFrameGrabber"), pollIntervalMs(pollMs), manager(manager), parser(parser) : juce::Thread("SyphonFrameGrabber"), pollIntervalMs(pollMs), manager(manager), parser(parser) {
{
// Create the invisible OpenGL context component // Create the invisible OpenGL context component
glContextComponent = std::make_unique<InvisibleOpenGLContextComponent>(); glContextComponent = std::make_unique<InvisibleOpenGLContextComponent>();
receiver = manager.addReceiver(server, app); receiver = manager.addReceiver(server, app);
@ -45,13 +44,11 @@ public:
} }
} }
bool isActive() const bool isActive() const {
{
return receiver != nullptr && receiver->isInit && receiver->enabled; return receiver != nullptr && receiver->isInit && receiver->enabled;
} }
juce::String getSourceName() const juce::String getSourceName() const {
{
if (receiver) { if (receiver) {
return receiver->sharingName + " (" + receiver->sharingAppName + ")"; return receiver->sharingName + " (" + receiver->sharingAppName + ")";
} }

Wyświetl plik

@ -1,14 +1,12 @@
#include "../LookAndFeel.h"
#include "VisualiserComponent.h" #include "VisualiserComponent.h"
#include "../CommonPluginProcessor.h"
#include "../CommonPluginEditor.h"
#include "../CommonPluginEditor.h"
#include "../CommonPluginProcessor.h"
#include "../LookAndFeel.h"
#include "AfterglowFragmentShader.glsl" #include "AfterglowFragmentShader.glsl"
#include "AfterglowVertexShader.glsl" #include "AfterglowVertexShader.glsl"
#include "BlurFragmentShader.glsl" #include "BlurFragmentShader.glsl"
#include "BlurVertexShader.glsl" #include "BlurVertexShader.glsl"
#include "WideBlurFragmentShader.glsl"
#include "WideBlurVertexShader.glsl"
#include "GlowFragmentShader.glsl" #include "GlowFragmentShader.glsl"
#include "GlowVertexShader.glsl" #include "GlowVertexShader.glsl"
#include "LineFragmentShader.glsl" #include "LineFragmentShader.glsl"
@ -19,29 +17,31 @@
#include "SimpleVertexShader.glsl" #include "SimpleVertexShader.glsl"
#include "TexturedFragmentShader.glsl" #include "TexturedFragmentShader.glsl"
#include "TexturedVertexShader.glsl" #include "TexturedVertexShader.glsl"
#include "WideBlurFragmentShader.glsl"
#include "WideBlurVertexShader.glsl"
VisualiserComponent::VisualiserComponent( VisualiserComponent::VisualiserComponent(
CommonAudioProcessor& processor, CommonAudioProcessor &processor,
CommonPluginEditor& pluginEditor, CommonPluginEditor &pluginEditor,
#if OSCI_PREMIUM #if OSCI_PREMIUM
SharedTextureManager& sharedTextureManager, SharedTextureManager &sharedTextureManager,
#endif #endif
juce::File ffmpegFile, juce::File ffmpegFile,
VisualiserSettings& settings, VisualiserSettings &settings,
RecordingSettings& recordingSettings, RecordingSettings &recordingSettings,
VisualiserComponent* parent, VisualiserComponent *parent,
bool visualiserOnly bool visualiserOnly) : audioProcessor(processor),
) : audioProcessor(processor), ffmpegFile(ffmpegFile),
ffmpegFile(ffmpegFile),
#if OSCI_PREMIUM #if OSCI_PREMIUM
sharedTextureManager(sharedTextureManager), sharedTextureManager(sharedTextureManager),
ffmpegEncoderManager(ffmpegFile),
#endif #endif
settings(settings), settings(settings),
recordingSettings(recordingSettings), recordingSettings(recordingSettings),
visualiserOnly(visualiserOnly), visualiserOnly(visualiserOnly),
osci::AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), processor.threadManager), osci::AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), processor.threadManager),
parent(parent), parent(parent),
editor(pluginEditor) { editor(pluginEditor) {
#if OSCI_PREMIUM #if OSCI_PREMIUM
addAndMakeVisible(editor.ffmpegDownloader); addAndMakeVisible(editor.ffmpegDownloader);
#endif #endif
@ -82,13 +82,11 @@ VisualiserComponent::VisualiserComponent(
sharedTextureButton.setTooltip("Toggles sending the oscilloscope's visuals to a Syphon/Spout receiver."); sharedTextureButton.setTooltip("Toggles sending the oscilloscope's visuals to a Syphon/Spout receiver.");
sharedTextureButton.onClick = [this] { sharedTextureButton.onClick = [this] {
if (sharedTextureSender != nullptr) { if (sharedTextureSender != nullptr) {
openGLContext.executeOnGLThread([this](juce::OpenGLContext& context) { openGLContext.executeOnGLThread([this](juce::OpenGLContext &context) { closeSharedTexture(); },
closeSharedTexture(); false);
}, false);
} else { } else {
openGLContext.executeOnGLThread([this](juce::OpenGLContext& context) { openGLContext.executeOnGLThread([this](juce::OpenGLContext &context) { initialiseSharedTexture(); },
initialiseSharedTexture(); false);
}, false);
} }
}; };
#endif #endif
@ -113,9 +111,7 @@ VisualiserComponent::VisualiserComponent(
audioInputButton.setClickingTogglesState(false); audioInputButton.setClickingTogglesState(false);
audioInputButton.setToggleState(!audioPlayer.isInitialised(), juce::NotificationType::dontSendNotification); audioInputButton.setToggleState(!audioPlayer.isInitialised(), juce::NotificationType::dontSendNotification);
audioPlayer.onParserChanged = [this] { audioPlayer.onParserChanged = [this] {
juce::MessageManager::callAsync([this] { juce::MessageManager::callAsync([this] { audioInputButton.setToggleState(!audioPlayer.isInitialised(), juce::NotificationType::dontSendNotification); });
audioInputButton.setToggleState(!audioPlayer.isInitialised(), juce::NotificationType::dontSendNotification);
});
}; };
audioInputButton.onClick = [this] { audioInputButton.onClick = [this] {
audioProcessor.stopAudioFile(); audioProcessor.stopAudioFile();
@ -124,7 +120,7 @@ VisualiserComponent::VisualiserComponent(
addChildComponent(audioPlayer); addChildComponent(audioPlayer);
audioPlayer.setVisible(visualiserOnly); audioPlayer.setVisible(visualiserOnly);
audioPlayer.addMouseListener(static_cast<juce::Component*>(this), true); audioPlayer.addMouseListener(static_cast<juce::Component *>(this), true);
openGLContext.setRenderer(this); openGLContext.setRenderer(this);
openGLContext.attachTo(*this); openGLContext.attachTo(*this);
@ -138,9 +134,7 @@ VisualiserComponent::~VisualiserComponent() {
audioProcessor.haltRecording = nullptr; audioProcessor.haltRecording = nullptr;
} }
openGLContext.detach(); openGLContext.detach();
setShouldBeRunning(false, [this] { setShouldBeRunning(false, [this] { renderingSemaphore.release(); });
renderingSemaphore.release();
});
} }
void VisualiserComponent::setFullScreen(bool fullScreen) { void VisualiserComponent::setFullScreen(bool fullScreen) {
@ -165,13 +159,13 @@ void VisualiserComponent::enableFullScreen() {
grabKeyboardFocus(); grabKeyboardFocus();
} }
void VisualiserComponent::mouseDoubleClick(const juce::MouseEvent& event) { void VisualiserComponent::mouseDoubleClick(const juce::MouseEvent &event) {
if (event.originalComponent == this) { if (event.originalComponent == this) {
enableFullScreen(); enableFullScreen();
} }
} }
void VisualiserComponent::runTask(const std::vector<osci::Point>& points) { void VisualiserComponent::runTask(const std::vector<osci::Point> &points) {
{ {
juce::CriticalSection::ScopedLockType lock(samplesLock); juce::CriticalSection::ScopedLockType lock(samplesLock);
@ -187,7 +181,7 @@ void VisualiserComponent::runTask(const std::vector<osci::Point>& points) {
zSamples.clear(); zSamples.clear();
auto applyEffects = [&](osci::Point point) { auto applyEffects = [&](osci::Point point) {
for (auto& effect : settings.parameters.audioEffects) { for (auto &effect : settings.parameters.audioEffects) {
point = effect->apply(0, point); point = effect->apply(0, point);
} }
#if OSCI_PREMIUM #if OSCI_PREMIUM
@ -208,7 +202,7 @@ void VisualiserComponent::runTask(const std::vector<osci::Point>& points) {
double triggerValue = settings.getTriggerValue(); double triggerValue = settings.getTriggerValue();
bool belowTrigger = false; bool belowTrigger = false;
for (const osci::Point& point : points) { for (const osci::Point &point : points) {
long samplePosition = sampleCount - lastTriggerPosition; long samplePosition = sampleCount - lastTriggerPosition;
double startPoint = 1.135; double startPoint = 1.135;
double sweep = samplePosition * sweepIncrement * 2 * startPoint - startPoint; double sweep = samplePosition * sweepIncrement * 2 * startPoint - startPoint;
@ -231,7 +225,7 @@ void VisualiserComponent::runTask(const std::vector<osci::Point>& points) {
sampleCount++; sampleCount++;
} }
} else { } else {
for (const osci::Point& rawPoint : points) { for (const osci::Point &rawPoint : points) {
osci::Point point = applyEffects(rawPoint); osci::Point point = applyEffects(rawPoint);
#if OSCI_PREMIUM #if OSCI_PREMIUM
@ -266,7 +260,7 @@ void VisualiserComponent::runTask(const std::vector<osci::Point>& points) {
double thisSample = xSamples[index]; double thisSample = xSamples[index];
double nextSample = xSamples[index + 1]; double nextSample = xSamples[index + 1];
if (nextSample > thisSample) { if (nextSample > thisSample) {
smoothedXSamples[i] = xSamples[index] + (i % (int) RESAMPLE_RATIO) * (nextSample - thisSample) / RESAMPLE_RATIO; smoothedXSamples[i] = xSamples[index] + (i % (int)RESAMPLE_RATIO) * (nextSample - thisSample) / RESAMPLE_RATIO;
} else { } else {
smoothedXSamples[i] = xSamples[index]; smoothedXSamples[i] = xSamples[index];
} }
@ -323,11 +317,11 @@ void VisualiserComponent::setPaused(bool paused, bool affectAudio) {
repaint(); repaint();
} }
void VisualiserComponent::mouseDrag(const juce::MouseEvent& event) { void VisualiserComponent::mouseDrag(const juce::MouseEvent &event) {
timerId = -1; timerId = -1;
} }
void VisualiserComponent::mouseMove(const juce::MouseEvent& event) { void VisualiserComponent::mouseMove(const juce::MouseEvent &event) {
if (event.getScreenX() == lastMouseX && event.getScreenY() == lastMouseY) { if (event.getScreenX() == lastMouseX && event.getScreenY() == lastMouseY) {
return; return;
} }
@ -356,14 +350,13 @@ void VisualiserComponent::mouseMove(const juce::MouseEvent& event) {
resized(); resized();
} }
} }
} } });
});
} }
resized(); resized();
} }
} }
void VisualiserComponent::mouseDown(const juce::MouseEvent& event) { void VisualiserComponent::mouseDown(const juce::MouseEvent &event) {
if (event.originalComponent == this) { if (event.originalComponent == this) {
if (event.mods.isLeftButtonDown() && child == nullptr && !record.getToggleState()) { if (event.mods.isLeftButtonDown() && child == nullptr && !record.getToggleState()) {
setPaused(active); setPaused(active);
@ -371,7 +364,7 @@ void VisualiserComponent::mouseDown(const juce::MouseEvent& event) {
} }
} }
bool VisualiserComponent::keyPressed(const juce::KeyPress& key) { bool VisualiserComponent::keyPressed(const juce::KeyPress &key) {
if (key.isKeyCode(juce::KeyPress::escapeKey)) { if (key.isKeyCode(juce::KeyPress::escapeKey)) {
if (fullScreenCallback) { if (fullScreenCallback) {
fullScreenCallback(FullScreenMode::MAIN_COMPONENT); fullScreenCallback(FullScreenMode::MAIN_COMPONENT);
@ -417,15 +410,13 @@ void VisualiserComponent::setRecording(bool recording) {
downloading = false; downloading = false;
resized(); resized();
}); });
}); }); });
});
}; };
auto onDownloadStart = [this] { auto onDownloadStart = [this] {
juce::MessageManager::callAsync([this] { juce::MessageManager::callAsync([this] {
record.setEnabled(false); record.setEnabled(false);
downloading = true; downloading = true;
resized(); resized(); });
});
}; };
if (!audioProcessor.ensureFFmpegExists(onDownloadStart, onDownloadSuccess)) { if (!audioProcessor.ensureFFmpegExists(onDownloadStart, onDownloadSuccess)) {
record.setToggleState(false, juce::NotificationType::dontSendNotification); record.setToggleState(false, juce::NotificationType::dontSendNotification);
@ -436,47 +427,16 @@ void VisualiserComponent::setRecording(bool recording) {
juce::String fileExtension = recordingSettings.getFileExtensionForCodec(); juce::String fileExtension = recordingSettings.getFileExtensionForCodec();
tempVideoFile = std::make_unique<juce::TemporaryFile>("." + fileExtension); tempVideoFile = std::make_unique<juce::TemporaryFile>("." + fileExtension);
juce::String resolution = std::to_string(renderTexture.width) + "x" + std::to_string(renderTexture.height);
juce::String cmd = "\"" + ffmpegFile.getFullPathName() + "\"" +
" -r " + juce::String(recordingSettings.getFrameRate()) +
" -f rawvideo" +
" -pix_fmt rgba" +
" -s " + resolution +
" -i -" +
" -threads 4" +
" -preset " + recordingSettings.getCompressionPreset() +
" -y" +
" -pix_fmt yuv420p";
// Apply codec-specific parameters
VideoCodec codec = recordingSettings.getVideoCodec(); VideoCodec codec = recordingSettings.getVideoCodec();
if (codec == VideoCodec::H264) { juce::String cmd = ffmpegEncoderManager.buildVideoEncodingCommand(
cmd += " -c:v libx264"; codec,
cmd += " -crf " + juce::String(recordingSettings.getCRF()); recordingSettings.getCRF(),
} else if (codec == VideoCodec::H265) { recordingSettings.getVideoToolboxQuality(),
cmd += " -c:v libx265"; renderTexture.width,
cmd += " -crf " + juce::String(recordingSettings.getCRF()); renderTexture.height,
#if JUCE_MAC && JUCE_ARM recordingSettings.getFrameRate(),
// use hardware encoding on Apple Silicon recordingSettings.getCompressionPreset(),
cmd += " -c:v hevc_videotoolbox"; tempVideoFile->getFile());
cmd += " -q:v " + juce::String(recordingSettings.getVideoToolboxQuality());
cmd += " -tag:v hvc1";
#endif
} else if (codec == VideoCodec::VP9) {
cmd += " -c:v libvpx-vp9";
cmd += " -b:v 0";
cmd += " -crf " + juce::String(recordingSettings.getCRF());
cmd += " -deadline good -cpu-used 2";
}
#if JUCE_MAC
else if (codec == VideoCodec::ProRes) {
cmd += " -c:v prores";
cmd += " -profile:v 3"; // ProRes 422 HQ
}
#endif
cmd += " -vf vflip";
cmd += " \"" + tempVideoFile->getFile().getFullPathName() + "\"";
ffmpegProcess.start(cmd); ffmpegProcess.start(cmd);
framePixels.resize(renderTexture.width * renderTexture.height * 4); framePixels.resize(renderTexture.width * renderTexture.height * 4);
@ -516,7 +476,7 @@ void VisualiserComponent::setRecording(bool recording) {
auto flags = juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::canSelectFiles | juce::FileBrowserComponent::warnAboutOverwriting; auto flags = juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::canSelectFiles | juce::FileBrowserComponent::warnAboutOverwriting;
#if OSCI_PREMIUM #if OSCI_PREMIUM
chooser->launchAsync(flags, [this, wasRecordingAudio, wasRecordingVideo](const juce::FileChooser& chooser) { chooser->launchAsync(flags, [this, wasRecordingAudio, wasRecordingVideo](const juce::FileChooser &chooser) {
auto file = chooser.getResult(); auto file = chooser.getResult();
if (file != juce::File()) { if (file != juce::File()) {
if (wasRecordingAudio && wasRecordingVideo) { if (wasRecordingAudio && wasRecordingVideo) {
@ -528,16 +488,14 @@ void VisualiserComponent::setRecording(bool recording) {
tempVideoFile->getFile().copyFileTo(file); tempVideoFile->getFile().copyFileTo(file);
} }
audioProcessor.setLastOpenedDirectory(file.getParentDirectory()); audioProcessor.setLastOpenedDirectory(file.getParentDirectory());
} } });
});
#else #else
chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) { chooser->launchAsync(flags, [this](const juce::FileChooser &chooser) {
auto file = chooser.getResult(); auto file = chooser.getResult();
if (file != juce::File()) { if (file != juce::File()) {
tempAudioFile->getFile().copyFileTo(file); tempAudioFile->getFile().copyFileTo(file);
audioProcessor.setLastOpenedDirectory(file.getParentDirectory()); audioProcessor.setLastOpenedDirectory(file.getParentDirectory());
} } });
});
#endif #endif
} }
@ -625,8 +583,7 @@ void VisualiserComponent::popoutWindow() {
settings, settings,
recordingSettings, recordingSettings,
this, this,
visualiserOnly visualiserOnly);
);
visualiser->settings.setLookAndFeel(&getLookAndFeel()); visualiser->settings.setLookAndFeel(&getLookAndFeel());
visualiser->openSettings = openSettings; visualiser->openSettings = openSettings;
visualiser->closeSettings = closeSettings; visualiser->closeSettings = closeSettings;
@ -671,8 +628,7 @@ void VisualiserComponent::initialiseSharedTexture() {
sharedTextureSender->setSharedTextureId(renderTexture.id); sharedTextureSender->setSharedTextureId(renderTexture.id);
sharedTextureSender->setDrawFunction([this] { sharedTextureSender->setDrawFunction([this] {
setShader(texturedShader.get()); setShader(texturedShader.get());
drawTexture({renderTexture}); drawTexture({renderTexture}); });
});
} }
void VisualiserComponent::closeSharedTexture() { void VisualiserComponent::closeSharedTexture() {
@ -680,7 +636,6 @@ void VisualiserComponent::closeSharedTexture() {
sharedTextureManager.removeSender(sharedTextureSender); sharedTextureManager.removeSender(sharedTextureSender);
sharedTextureSender = nullptr; sharedTextureSender = nullptr;
} }
} }
#endif #endif
@ -695,7 +650,7 @@ void VisualiserComponent::newOpenGLContextCreated() {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD); glBlendEquation(GL_FUNC_ADD);
fullScreenQuad = { -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f }; fullScreenQuad = {-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f};
simpleShader = std::make_unique<juce::OpenGLShaderProgram>(openGLContext); simpleShader = std::make_unique<juce::OpenGLShaderProgram>(openGLContext);
simpleShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(simpleVertexShader)); simpleShader->addVertexShader(juce::OpenGLHelpers::translateVertexShaderToV3(simpleVertexShader));
@ -921,7 +876,7 @@ void VisualiserComponent::setupTextures() {
renderTexture = makeTexture(recordingSettings.getResolution(), recordingSettings.getResolution()); renderTexture = makeTexture(recordingSettings.getResolution(), recordingSettings.getResolution());
screenOpenGLTexture.loadImage(emptyScreenImage); screenOpenGLTexture.loadImage(emptyScreenImage);
screenTexture = { screenOpenGLTexture.getTextureID(), screenTextureImage.getWidth(), screenTextureImage.getHeight() }; screenTexture = {screenOpenGLTexture.getTextureID(), screenTextureImage.getWidth(), screenTextureImage.getHeight()};
#if OSCI_PREMIUM #if OSCI_PREMIUM
glowTexture = makeTexture(512, 512); glowTexture = makeTexture(512, 512);
@ -946,7 +901,7 @@ Texture VisualiserComponent::makeTexture(int width, int height, GLuint textureID
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float borderColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; float borderColor[] = {0.0f, 0.0f, 0.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
@ -958,7 +913,7 @@ Texture VisualiserComponent::makeTexture(int width, int height, GLuint textureID
glBindTexture(GL_TEXTURE_2D, 0); // Unbind glBindTexture(GL_TEXTURE_2D, 0); // Unbind
return { textureID, width, height }; return {textureID, width, height};
} }
void VisualiserComponent::setResolution(int width) { void VisualiserComponent::setResolution(int width) {
@ -975,7 +930,7 @@ void VisualiserComponent::setResolution(int width) {
glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind
} }
void VisualiserComponent::drawLineTexture(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints) { void VisualiserComponent::drawLineTexture(const std::vector<float> &xPoints, const std::vector<float> &yPoints, const std::vector<float> &zPoints) {
using namespace juce::gl; using namespace juce::gl;
double persistence = std::pow(0.5, settings.getPersistence()) * 0.4; double persistence = std::pow(0.5, settings.getPersistence()) * 0.4;
@ -988,7 +943,7 @@ void VisualiserComponent::drawLineTexture(const std::vector<float>& xPoints, con
glBindTexture(GL_TEXTURE_2D, targetTexture.value().id); glBindTexture(GL_TEXTURE_2D, targetTexture.value().id);
} }
void VisualiserComponent::saveTextureToPNG(Texture texture, const juce::File& file) { void VisualiserComponent::saveTextureToPNG(Texture texture, const juce::File &file) {
using namespace juce::gl; using namespace juce::gl;
GLuint textureID = texture.id; GLuint textureID = texture.id;
int width = texture.width; int width = texture.width;
@ -1000,20 +955,20 @@ void VisualiserComponent::saveTextureToPNG(Texture texture, const juce::File& fi
// Read the pixels from the texture // Read the pixels from the texture
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
juce::Image image = juce::Image (juce::Image::PixelFormat::ARGB, width, height, true); juce::Image image = juce::Image(juce::Image::PixelFormat::ARGB, width, height, true);
juce::Image::BitmapData bitmapData(image, juce::Image::BitmapData::writeOnly); juce::Image::BitmapData bitmapData(image, juce::Image::BitmapData::writeOnly);
// Copy the pixel data to the JUCE image (and swap R and B channels) // Copy the pixel data to the JUCE image (and swap R and B channels)
for (int y = 0; y < height; ++y) { for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {
int srcIndex = (y * width + x) * 4; // RGBA format int srcIndex = (y * width + x) * 4; // RGBA format
juce::uint8 r = (pixels)[srcIndex]; // Red juce::uint8 r = (pixels)[srcIndex]; // Red
juce::uint8 g = (pixels)[srcIndex + 1]; // Green juce::uint8 g = (pixels)[srcIndex + 1]; // Green
juce::uint8 b = (pixels)[srcIndex + 2]; // Blue juce::uint8 b = (pixels)[srcIndex + 2]; // Blue
juce::uint8 a = (pixels)[srcIndex + 3]; // Alpha juce::uint8 a = (pixels)[srcIndex + 3]; // Alpha
// This method uses colors in RGBA // This method uses colors in RGBA
bitmapData.setPixelColour(x, height-y-1, juce::Colour(r, g, b, a)); bitmapData.setPixelColour(x, height - y - 1, juce::Colour(r, g, b, a));
} }
} }
@ -1029,7 +984,7 @@ void VisualiserComponent::saveTextureToPNG(Texture texture, const juce::File& fi
} }
} }
void VisualiserComponent::saveTextureToQOI(Texture texture, const juce::File& file) { void VisualiserComponent::saveTextureToQOI(Texture texture, const juce::File &file) {
using namespace juce::gl; using namespace juce::gl;
GLuint textureID = texture.id; GLuint textureID = texture.id;
int width = texture.width; int width = texture.width;
@ -1041,7 +996,7 @@ void VisualiserComponent::saveTextureToQOI(Texture texture, const juce::File& fi
// Read the pixels from the texture // Read the pixels from the texture
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
const qoixx::qoi::desc imageFormat{ .width = (uint32_t) width, .height = (uint32_t) height, .channels = 4, .colorspace = qoixx::qoi::colorspace::srgb }; const qoixx::qoi::desc imageFormat{.width = (uint32_t)width, .height = (uint32_t)height, .channels = 4, .colorspace = qoixx::qoi::colorspace::srgb};
std::vector<unsigned char> binaryData = qoixx::qoi::encode<std::vector<unsigned char>>(pixels, imageFormat); std::vector<unsigned char> binaryData = qoixx::qoi::encode<std::vector<unsigned char>>(pixels, imageFormat);
file.replaceWithData(binaryData.data(), binaryData.size()); file.replaceWithData(binaryData.data(), binaryData.size());
} }
@ -1060,7 +1015,7 @@ void VisualiserComponent::activateTargetTexture(std::optional<Texture> texture)
targetTexture = texture; targetTexture = texture;
} }
void VisualiserComponent::setShader(juce::OpenGLShaderProgram* program) { void VisualiserComponent::setShader(juce::OpenGLShaderProgram *program) {
currentShader = program; currentShader = program;
program->use(); program->use();
} }
@ -1103,7 +1058,7 @@ void VisualiserComponent::setNormalBlending() {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} }
void VisualiserComponent::drawLine(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints) { void VisualiserComponent::drawLine(const std::vector<float> &xPoints, const std::vector<float> &yPoints, const std::vector<float> &zPoints) {
using namespace juce::gl; using namespace juce::gl;
setAdditiveBlending(); setAdditiveBlending();
@ -1111,11 +1066,12 @@ void VisualiserComponent::drawLine(const std::vector<float>& xPoints, const std:
int nPoints = xPoints.size(); int nPoints = xPoints.size();
// Without this, there's an access violation that seems to occur only on some systems // Without this, there's an access violation that seems to occur only on some systems
if (scratchVertices.size() != nPoints * 12) scratchVertices.resize(nPoints * 12); if (scratchVertices.size() != nPoints * 12)
scratchVertices.resize(nPoints * 12);
for (int i = 0; i < nPoints; ++i) { for (int i = 0; i < nPoints; ++i) {
int p = i * 12; int p = i * 12;
scratchVertices[p] = scratchVertices[p + 3] = scratchVertices[p + 6] = scratchVertices[p + 9] = xPoints[i]; scratchVertices[p] = scratchVertices[p + 3] = scratchVertices[p + 6] = scratchVertices[p + 9] = xPoints[i];
scratchVertices[p + 1] = scratchVertices[p + 4] = scratchVertices[p + 7] = scratchVertices[p + 10] = yPoints[i]; scratchVertices[p + 1] = scratchVertices[p + 4] = scratchVertices[p + 7] = scratchVertices[p + 10] = yPoints[i];
scratchVertices[p + 2] = scratchVertices[p + 5] = scratchVertices[p + 8] = scratchVertices[p + 11] = zPoints[i]; scratchVertices[p + 2] = scratchVertices[p + 5] = scratchVertices[p + 8] = scratchVertices[p + 11] = zPoints[i];
} }
@ -1131,25 +1087,25 @@ void VisualiserComponent::drawLine(const std::vector<float>& xPoints, const std:
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(glGetAttribLocation(lineShader->getProgramID(), "aStart"), 3, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribPointer(glGetAttribLocation(lineShader->getProgramID(), "aStart"), 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(glGetAttribLocation(lineShader->getProgramID(), "aEnd"), 3, GL_FLOAT, GL_FALSE, 0, (void*)(12 * sizeof(float))); glVertexAttribPointer(glGetAttribLocation(lineShader->getProgramID(), "aEnd"), 3, GL_FLOAT, GL_FALSE, 0, (void *)(12 * sizeof(float)));
glBindBuffer(GL_ARRAY_BUFFER, quadIndexBuffer); glBindBuffer(GL_ARRAY_BUFFER, quadIndexBuffer);
glVertexAttribPointer(glGetAttribLocation(lineShader->getProgramID(), "aIdx"), 1, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribPointer(glGetAttribLocation(lineShader->getProgramID(), "aIdx"), 1, GL_FLOAT, GL_FALSE, 0, 0);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, screenTexture.id); glBindTexture(GL_TEXTURE_2D, screenTexture.id);
lineShader->setUniform("uScreen", 0); lineShader->setUniform("uScreen", 0);
lineShader->setUniform("uSize", (GLfloat) settings.getFocus()); lineShader->setUniform("uSize", (GLfloat)settings.getFocus());
lineShader->setUniform("uGain", 450.0f / 512.0f); lineShader->setUniform("uGain", 450.0f / 512.0f);
lineShader->setUniform("uInvert", 1.0f); lineShader->setUniform("uInvert", 1.0f);
if (settings.getUpsamplingEnabled()) { if (settings.getUpsamplingEnabled()) {
lineShader->setUniform("uIntensity", intensity); lineShader->setUniform("uIntensity", intensity);
} else { } else {
lineShader->setUniform("uIntensity", (GLfloat) (intensity * RESAMPLE_RATIO * 1.5)); lineShader->setUniform("uIntensity", (GLfloat)(intensity * RESAMPLE_RATIO * 1.5));
} }
lineShader->setUniform("uFadeAmount", fadeAmount); lineShader->setUniform("uFadeAmount", fadeAmount);
lineShader->setUniform("uNEdges", (GLfloat) nEdges); lineShader->setUniform("uNEdges", (GLfloat)nEdges);
setOffsetAndScale(lineShader.get()); setOffsetAndScale(lineShader.get());
#if OSCI_PREMIUM #if OSCI_PREMIUM
@ -1177,8 +1133,8 @@ void VisualiserComponent::fade() {
#if OSCI_PREMIUM #if OSCI_PREMIUM
setShader(afterglowShader.get()); setShader(afterglowShader.get());
afterglowShader->setUniform("fadeAmount", fadeAmount); afterglowShader->setUniform("fadeAmount", fadeAmount);
afterglowShader->setUniform("afterglowAmount", (float) settings.getAfterglow()); afterglowShader->setUniform("afterglowAmount", (float)settings.getAfterglow());
afterglowShader->setUniform("uResizeForCanvas", lineTexture.width / (float) recordingSettings.getResolution()); afterglowShader->setUniform("uResizeForCanvas", lineTexture.width / (float)recordingSettings.getResolution());
drawTexture({lineTexture}); drawTexture({lineTexture});
#else #else
simpleShader->use(); simpleShader->use();
@ -1201,33 +1157,33 @@ void VisualiserComponent::drawCRT() {
activateTargetTexture(blur1Texture); activateTargetTexture(blur1Texture);
setShader(texturedShader.get()); setShader(texturedShader.get());
texturedShader->setUniform("uResizeForCanvas", lineTexture.width / (float) recordingSettings.getResolution()); texturedShader->setUniform("uResizeForCanvas", lineTexture.width / (float)recordingSettings.getResolution());
drawTexture({lineTexture}); drawTexture({lineTexture});
//horizontal blur 512x512 // horizontal blur 512x512
activateTargetTexture(blur2Texture); activateTargetTexture(blur2Texture);
setShader(blurShader.get()); setShader(blurShader.get());
blurShader->setUniform("uOffset", 1.0f / 512.0f, 0.0f); blurShader->setUniform("uOffset", 1.0f / 512.0f, 0.0f);
drawTexture({blur1Texture}); drawTexture({blur1Texture});
//vertical blur 512x512 // vertical blur 512x512
activateTargetTexture(blur1Texture); activateTargetTexture(blur1Texture);
blurShader->setUniform("uOffset", 0.0f, 1.0f / 512.0f); blurShader->setUniform("uOffset", 0.0f, 1.0f / 512.0f);
drawTexture({blur2Texture}); drawTexture({blur2Texture});
//preserve blur1 for later // preserve blur1 for later
activateTargetTexture(blur3Texture); activateTargetTexture(blur3Texture);
setShader(texturedShader.get()); setShader(texturedShader.get());
texturedShader->setUniform("uResizeForCanvas", 1.0f); texturedShader->setUniform("uResizeForCanvas", 1.0f);
drawTexture({blur1Texture}); drawTexture({blur1Texture});
//horizontal blur 128x128 // horizontal blur 128x128
activateTargetTexture(blur4Texture); activateTargetTexture(blur4Texture);
setShader(wideBlurShader.get()); setShader(wideBlurShader.get());
wideBlurShader->setUniform("uOffset", 1.0f / 128.0f, 0.0f); wideBlurShader->setUniform("uOffset", 1.0f / 128.0f, 0.0f);
drawTexture({blur3Texture}); drawTexture({blur3Texture});
//vertical blur 128x128 // vertical blur 128x128
activateTargetTexture(blur3Texture); activateTargetTexture(blur3Texture);
wideBlurShader->setUniform("uOffset", 0.0f, 1.0f / 128.0f); wideBlurShader->setUniform("uOffset", 0.0f, 1.0f / 128.0f);
drawTexture({blur4Texture}); drawTexture({blur4Texture});
@ -1245,26 +1201,26 @@ void VisualiserComponent::drawCRT() {
activateTargetTexture(renderTexture); activateTargetTexture(renderTexture);
setShader(outputShader.get()); setShader(outputShader.get());
outputShader->setUniform("uExposure", 0.25f); outputShader->setUniform("uExposure", 0.25f);
outputShader->setUniform("uLineSaturation", (float) settings.getLineSaturation()); outputShader->setUniform("uLineSaturation", (float)settings.getLineSaturation());
#if OSCI_PREMIUM #if OSCI_PREMIUM
outputShader->setUniform("uScreenSaturation", (float) settings.getScreenSaturation()); outputShader->setUniform("uScreenSaturation", (float)settings.getScreenSaturation());
outputShader->setUniform("uHueShift", (float) settings.getScreenHue() / 360.0f); outputShader->setUniform("uHueShift", (float)settings.getScreenHue() / 360.0f);
outputShader->setUniform("uOverexposure", (float) settings.getOverexposure()); outputShader->setUniform("uOverexposure", (float)settings.getOverexposure());
#else #else
outputShader->setUniform("uScreenSaturation", 1.0f); outputShader->setUniform("uScreenSaturation", 1.0f);
outputShader->setUniform("uHueShift", 0.0f); outputShader->setUniform("uHueShift", 0.0f);
outputShader->setUniform("uOverexposure", 0.5f); outputShader->setUniform("uOverexposure", 0.5f);
#endif #endif
outputShader->setUniform("uNoise", (float) settings.getNoise()); outputShader->setUniform("uNoise", (float)settings.getNoise());
outputShader->setUniform("uRandom", juce::Random::getSystemRandom().nextFloat()); outputShader->setUniform("uRandom", juce::Random::getSystemRandom().nextFloat());
outputShader->setUniform("uGlow", (float) settings.getGlow()); outputShader->setUniform("uGlow", (float)settings.getGlow());
outputShader->setUniform("uAmbient", (float) settings.getAmbient()); outputShader->setUniform("uAmbient", (float)settings.getAmbient());
setOffsetAndScale(outputShader.get()); setOffsetAndScale(outputShader.get());
#if OSCI_PREMIUM #if OSCI_PREMIUM
outputShader->setUniform("uFishEye", screenOverlay == ScreenOverlay::VectorDisplay ? VECTOR_DISPLAY_FISH_EYE : 0.0f); outputShader->setUniform("uFishEye", screenOverlay == ScreenOverlay::VectorDisplay ? VECTOR_DISPLAY_FISH_EYE : 0.0f);
outputShader->setUniform("uRealScreen", settings.parameters.screenOverlay->isRealisticDisplay() ? 1.0f : 0.0f); outputShader->setUniform("uRealScreen", settings.parameters.screenOverlay->isRealisticDisplay() ? 1.0f : 0.0f);
#endif #endif
outputShader->setUniform("uResizeForCanvas", lineTexture.width / (float) recordingSettings.getResolution()); outputShader->setUniform("uResizeForCanvas", lineTexture.width / (float)recordingSettings.getResolution());
juce::Colour colour = juce::Colour::fromHSV(settings.getHue() / 360.0f, 1.0, 1.0, 1.0); juce::Colour colour = juce::Colour::fromHSV(settings.getHue() / 360.0f, 1.0, 1.0, 1.0);
outputShader->setUniform("uColour", colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue()); outputShader->setUniform("uColour", colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue());
drawTexture({ drawTexture({
@ -1279,9 +1235,9 @@ void VisualiserComponent::drawCRT() {
}); });
} }
void VisualiserComponent::setOffsetAndScale(juce::OpenGLShaderProgram* shader) { void VisualiserComponent::setOffsetAndScale(juce::OpenGLShaderProgram *shader) {
osci::Point offset; osci::Point offset;
osci::Point scale = { 1.0f }; osci::Point scale = {1.0f};
#if OSCI_PREMIUM #if OSCI_PREMIUM
if (settings.getScreenOverlay() == ScreenOverlay::Real) { if (settings.getScreenOverlay() == ScreenOverlay::Real) {
offset = REAL_SCREEN_OFFSET; offset = REAL_SCREEN_OFFSET;
@ -1291,8 +1247,8 @@ void VisualiserComponent::setOffsetAndScale(juce::OpenGLShaderProgram* shader) {
scale = VECTOR_DISPLAY_SCALE; scale = VECTOR_DISPLAY_SCALE;
} }
#endif #endif
shader->setUniform("uOffset", (float) offset.x, (float) offset.y); shader->setUniform("uOffset", (float)offset.x, (float)offset.y);
shader->setUniform("uScale", (float) scale.x, (float) scale.y); shader->setUniform("uScale", (float)scale.x, (float)scale.y);
} }
#if OSCI_PREMIUM #if OSCI_PREMIUM
@ -1307,7 +1263,7 @@ Texture VisualiserComponent::createReflectionTexture() {
reflectionOpenGLTexture.loadImage(emptyReflectionImage); reflectionOpenGLTexture.loadImage(emptyReflectionImage);
} }
Texture texture = { reflectionOpenGLTexture.getTextureID(), reflectionOpenGLTexture.getWidth(), reflectionOpenGLTexture.getHeight() }; Texture texture = {reflectionOpenGLTexture.getTextureID(), reflectionOpenGLTexture.getWidth(), reflectionOpenGLTexture.getHeight()};
return texture; return texture;
} }
@ -1328,7 +1284,7 @@ Texture VisualiserComponent::createScreenTexture() {
screenOpenGLTexture.loadImage(emptyScreenImage); screenOpenGLTexture.loadImage(emptyScreenImage);
} }
checkGLErrors(__FILE__, __LINE__); checkGLErrors(__FILE__, __LINE__);
Texture texture = { screenOpenGLTexture.getTextureID(), screenTextureImage.getWidth(), screenTextureImage.getHeight() }; Texture texture = {screenOpenGLTexture.getTextureID(), screenTextureImage.getWidth(), screenTextureImage.getHeight()};
if (screenOverlay == ScreenOverlay::Graticule || screenOverlay == ScreenOverlay::SmudgedGraticule) { if (screenOverlay == ScreenOverlay::Graticule || screenOverlay == ScreenOverlay::SmudgedGraticule) {
activateTargetTexture(texture); activateTargetTexture(texture);
@ -1366,7 +1322,8 @@ Texture VisualiserComponent::createScreenTexture() {
for (int j = 0; j < 51; j++) { for (int j = 0; j < 51; j++) {
float t = j * step / 5; float t = j * step / 5;
if (static_cast<int>(t) % 5 == 0) continue; if (static_cast<int>(t) % 5 == 0)
continue;
data.insert(data.begin(), {t - 2, 2.5f * step, t + 2, 2.5f * step}); data.insert(data.begin(), {t - 2, 2.5f * step, t + 2, 2.5f * step});
data.insert(data.begin(), {t - 2, 7.5f * step, t + 2, 7.5f * step}); data.insert(data.begin(), {t - 2, 7.5f * step, t + 2, 7.5f * step});
@ -1399,21 +1356,36 @@ void VisualiserComponent::checkGLErrors(juce::String file, int line) {
while ((error = glGetError()) != GL_NO_ERROR) { while ((error = glGetError()) != GL_NO_ERROR) {
juce::String errorMessage; juce::String errorMessage;
switch (error) { switch (error) {
case GL_INVALID_ENUM: errorMessage = "GL_INVALID_ENUM"; break; case GL_INVALID_ENUM:
case GL_INVALID_VALUE: errorMessage = "GL_INVALID_VALUE"; break; errorMessage = "GL_INVALID_ENUM";
case GL_INVALID_OPERATION: errorMessage = "GL_INVALID_OPERATION"; break; break;
case GL_STACK_OVERFLOW: errorMessage = "GL_STACK_OVERFLOW"; break; case GL_INVALID_VALUE:
case GL_STACK_UNDERFLOW: errorMessage = "GL_STACK_UNDERFLOW"; break; errorMessage = "GL_INVALID_VALUE";
case GL_OUT_OF_MEMORY: errorMessage = "GL_OUT_OF_MEMORY"; break; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: errorMessage = "GL_INVALID_FRAMEBUFFER_OPERATION"; break; case GL_INVALID_OPERATION:
default: errorMessage = "Unknown OpenGL error"; break; errorMessage = "GL_INVALID_OPERATION";
break;
case GL_STACK_OVERFLOW:
errorMessage = "GL_STACK_OVERFLOW";
break;
case GL_STACK_UNDERFLOW:
errorMessage = "GL_STACK_UNDERFLOW";
break;
case GL_OUT_OF_MEMORY:
errorMessage = "GL_OUT_OF_MEMORY";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
errorMessage = "GL_INVALID_FRAMEBUFFER_OPERATION";
break;
default:
errorMessage = "Unknown OpenGL error";
break;
} }
DBG("OpenGL error at " + file + ":" + juce::String(line) + " - " + errorMessage); DBG("OpenGL error at " + file + ":" + juce::String(line) + " - " + errorMessage);
} }
} }
void VisualiserComponent::paint(juce::Graphics &g) {
void VisualiserComponent::paint(juce::Graphics& g) {
g.setColour(Colours::veryDark); g.setColour(Colours::veryDark);
g.fillRect(buttonRow); g.fillRect(buttonRow);
if (!active) { if (!active) {
@ -1428,7 +1400,7 @@ void VisualiserComponent::paint(juce::Graphics& g) {
} }
} }
void VisualiserComponent::renderScope(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints) { void VisualiserComponent::renderScope(const std::vector<float> &xPoints, const std::vector<float> &yPoints, const std::vector<float> &zPoints) {
if (screenOverlay != settings.getScreenOverlay()) { if (screenOverlay != settings.getScreenOverlay()) {
screenOverlay = settings.getScreenOverlay(); screenOverlay = settings.getScreenOverlay();
#if OSCI_PREMIUM #if OSCI_PREMIUM

Wyświetl plik

@ -1,17 +1,20 @@
#pragma once #pragma once
#include <algorithm>
#include <JuceHeader.h> #include <JuceHeader.h>
#include <algorithm>
#include "../LookAndFeel.h" #include "../LookAndFeel.h"
#include "../components/SvgButton.h"
#include "VisualiserSettings.h"
#include "RecordingSettings.h"
#include "../components/StopwatchComponent.h"
#include "../img/qoixx.hpp"
#include "../components/DownloaderComponent.h"
#include "../audio/AudioRecorder.h" #include "../audio/AudioRecorder.h"
#include "../wav/WavParser.h"
#include "../components/AudioPlayerComponent.h" #include "../components/AudioPlayerComponent.h"
#include "../components/DownloaderComponent.h"
#include "../components/StopwatchComponent.h"
#include "../components/SvgButton.h"
#include "../img/qoixx.hpp"
#include "../video/FFmpegEncoderManager.h"
#include "../wav/WavParser.h"
#include "RecordingSettings.h"
#include "VisualiserSettings.h"
#define FILE_RENDER_DUMMY 0 #define FILE_RENDER_DUMMY 0
#define FILE_RENDER_PNG 1 #define FILE_RENDER_PNG 1
@ -44,8 +47,7 @@ public:
VisualiserSettings& settings, VisualiserSettings& settings,
RecordingSettings& recordingSettings, RecordingSettings& recordingSettings,
VisualiserComponent* parent = nullptr, VisualiserComponent* parent = nullptr,
bool visualiserOnly = false bool visualiserOnly = false);
);
~VisualiserComponent() override; ~VisualiserComponent() override;
std::function<void()> openSettings; std::function<void()> openSettings;
@ -87,13 +89,13 @@ private:
bool visualiserOnly; bool visualiserOnly;
AudioPlayerComponent audioPlayer{audioProcessor}; AudioPlayerComponent audioPlayer{audioProcessor};
SvgButton fullScreenButton{ "fullScreen", BinaryData::fullscreen_svg, juce::Colours::white, juce::Colours::white }; SvgButton fullScreenButton{"fullScreen", BinaryData::fullscreen_svg, juce::Colours::white, juce::Colours::white};
SvgButton popOutButton{ "popOut", BinaryData::open_in_new_svg, juce::Colours::white, juce::Colours::white }; 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 }; SvgButton settingsButton{"settings", BinaryData::cog_svg, juce::Colours::white, juce::Colours::white};
SvgButton audioInputButton{ "audioInput", BinaryData::microphone_svg, juce::Colours::white, juce::Colours::red }; SvgButton audioInputButton{"audioInput", BinaryData::microphone_svg, juce::Colours::white, juce::Colours::red};
#if OSCI_PREMIUM #if OSCI_PREMIUM
SvgButton sharedTextureButton{ "sharedTexture", BinaryData::spout_svg, juce::Colours::white, juce::Colours::red }; SvgButton sharedTextureButton{"sharedTexture", BinaryData::spout_svg, juce::Colours::white, juce::Colours::red};
SharedTextureManager& sharedTextureManager; SharedTextureManager& sharedTextureManager;
SharedTextureSender* sharedTextureSender = nullptr; SharedTextureSender* sharedTextureSender = nullptr;
#endif #endif
@ -118,6 +120,7 @@ private:
std::vector<unsigned char> framePixels; std::vector<unsigned char> framePixels;
osci::WriteProcess ffmpegProcess; osci::WriteProcess ffmpegProcess;
std::unique_ptr<juce::TemporaryFile> tempVideoFile; std::unique_ptr<juce::TemporaryFile> tempVideoFile;
FFmpegEncoderManager ffmpegEncoderManager;
#endif #endif
StopwatchComponent stopwatch; StopwatchComponent stopwatch;
@ -187,11 +190,11 @@ private:
juce::Image oscilloscopeReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::real_reflection_png, BinaryData::real_reflection_pngSize); juce::Image oscilloscopeReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::real_reflection_png, BinaryData::real_reflection_pngSize);
juce::Image vectorDisplayReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::vector_display_reflection_png, BinaryData::vector_display_reflection_pngSize); juce::Image vectorDisplayReflectionImage = juce::ImageFileFormat::loadFrom(BinaryData::vector_display_reflection_png, BinaryData::vector_display_reflection_pngSize);
osci::Point REAL_SCREEN_OFFSET = { 0.02, -0.15 }; osci::Point REAL_SCREEN_OFFSET = {0.02, -0.15};
osci::Point REAL_SCREEN_SCALE = { 0.6 }; osci::Point REAL_SCREEN_SCALE = {0.6};
osci::Point VECTOR_DISPLAY_OFFSET = { 0.075, -0.045 }; osci::Point VECTOR_DISPLAY_OFFSET = {0.075, -0.045};
osci::Point VECTOR_DISPLAY_SCALE = { 0.6 }; osci::Point VECTOR_DISPLAY_SCALE = {0.6};
float VECTOR_DISPLAY_FISH_EYE = 0.5; float VECTOR_DISPLAY_FISH_EYE = 0.5;
juce::OpenGLTexture reflectionOpenGLTexture; juce::OpenGLTexture reflectionOpenGLTexture;

@ -1 +1 @@
Subproject commit cf124cc5de4d9857c7633e9c03117f20e1550e81 Subproject commit f8ac3007c25df061ca6e71ad2eaff4a5d01e2d7b

Wyświetl plik

@ -574,6 +574,16 @@
<FILE id="mC1tUv" name="ugen_JuceUtility.h" compile="0" resource="0" <FILE id="mC1tUv" name="ugen_JuceUtility.h" compile="0" resource="0"
file="Source/UGen/ugen_JuceUtility.h"/> file="Source/UGen/ugen_JuceUtility.h"/>
</GROUP> </GROUP>
<GROUP id="{0F62E77C-5385-0C56-69A1-3C8866A6E6E3}" name="video">
<FILE id="DniMew" name="FFmpegEncoderManager.cpp" compile="1" resource="0"
file="Source/video/FFmpegEncoderManager.cpp"/>
<FILE id="t2oI5O" name="FFmpegEncoderManager.h" compile="0" resource="0"
file="Source/video/FFmpegEncoderManager.h"/>
<FILE id="xEIRCs" name="InvisibleOpenGLContextComponent.h" compile="0"
resource="0" file="Source/video/InvisibleOpenGLContextComponent.h"/>
<FILE id="OyC3qj" name="SyphonFrameGrabber.h" compile="0" resource="0"
file="Source/video/SyphonFrameGrabber.h"/>
</GROUP>
<GROUP id="{16A8DC64-BA02-898D-4DBA-AA3DDF6F9297}" name="visualiser"> <GROUP id="{16A8DC64-BA02-898D-4DBA-AA3DDF6F9297}" name="visualiser">
<FILE id="DkDKBX" name="AfterglowFragmentShader.glsl" compile="0" resource="0" <FILE id="DkDKBX" name="AfterglowFragmentShader.glsl" compile="0" resource="0"
file="Source/visualiser/AfterglowFragmentShader.glsl"/> file="Source/visualiser/AfterglowFragmentShader.glsl"/>
@ -644,8 +654,6 @@
file="Source/FrameSettingsComponent.cpp"/> file="Source/FrameSettingsComponent.cpp"/>
<FILE id="lzBNS1" name="FrameSettingsComponent.h" compile="0" resource="0" <FILE id="lzBNS1" name="FrameSettingsComponent.h" compile="0" resource="0"
file="Source/FrameSettingsComponent.h"/> file="Source/FrameSettingsComponent.h"/>
<FILE id="nfoWJk" name="InvisibleOpenGLContextComponent.h" compile="0"
resource="0" file="Source/InvisibleOpenGLContextComponent.h"/>
<FILE id="d2zFqF" name="LookAndFeel.cpp" compile="1" resource="0" file="Source/LookAndFeel.cpp"/> <FILE id="d2zFqF" name="LookAndFeel.cpp" compile="1" resource="0" file="Source/LookAndFeel.cpp"/>
<FILE id="TJDqWs" name="LookAndFeel.h" compile="0" resource="0" file="Source/LookAndFeel.h"/> <FILE id="TJDqWs" name="LookAndFeel.h" compile="0" resource="0" file="Source/LookAndFeel.h"/>
<FILE id="X26RjJ" name="LuaComponent.cpp" compile="1" resource="0" <FILE id="X26RjJ" name="LuaComponent.cpp" compile="1" resource="0"
@ -673,8 +681,6 @@
file="Source/SettingsComponent.cpp"/> file="Source/SettingsComponent.cpp"/>
<FILE id="Vlmozi" name="SettingsComponent.h" compile="0" resource="0" <FILE id="Vlmozi" name="SettingsComponent.h" compile="0" resource="0"
file="Source/SettingsComponent.h"/> file="Source/SettingsComponent.h"/>
<FILE id="jyHVpz" name="SyphonFrameGrabber.h" compile="0" resource="0"
file="Source/SyphonFrameGrabber.h"/>
<FILE id="UxZu4n" name="TxtComponent.cpp" compile="1" resource="0" <FILE id="UxZu4n" name="TxtComponent.cpp" compile="1" resource="0"
file="Source/TxtComponent.cpp"/> file="Source/TxtComponent.cpp"/>
<FILE id="kxPbsL" name="TxtComponent.h" compile="0" resource="0" file="Source/TxtComponent.h"/> <FILE id="kxPbsL" name="TxtComponent.h" compile="0" resource="0" file="Source/TxtComponent.h"/>

Wyświetl plik

@ -77,6 +77,12 @@
</GROUP> </GROUP>
</GROUP> </GROUP>
<GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source"> <GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source">
<GROUP id="{34BCEBE9-062C-27E1-5661-B33652D8F4F5}" name="video">
<FILE id="pmHHqY" name="FFmpegEncoderManager.cpp" compile="1" resource="0"
file="Source/video/FFmpegEncoderManager.cpp"/>
<FILE id="oKPzgR" name="FFmpegEncoderManager.h" compile="0" resource="0"
file="Source/video/FFmpegEncoderManager.h"/>
</GROUP>
<FILE id="fqqP0r" name="CustomStandalone.cpp" compile="1" resource="0" <FILE id="fqqP0r" name="CustomStandalone.cpp" compile="1" resource="0"
file="Source/CustomStandalone.cpp"/> file="Source/CustomStandalone.cpp"/>
<FILE id="TFmWW0" name="CustomStandaloneFilterWindow.h" compile="0" <FILE id="TFmWW0" name="CustomStandaloneFilterWindow.h" compile="0"