kopia lustrzana https://github.com/jameshball/osci-render
commit
9460737544
|
@ -1,6 +1,6 @@
|
|||
#include "CommonPluginProcessor.h"
|
||||
#include "CommonPluginEditor.h"
|
||||
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
|
||||
#include "CustomStandaloneFilterWindow.h"
|
||||
|
||||
CommonPluginEditor::CommonPluginEditor(CommonAudioProcessor& p, juce::String appName, juce::String projectFileType, int defaultWidth, int defaultHeight)
|
||||
: AudioProcessorEditor(&p), audioProcessor(p), appName(appName), projectFileType(projectFileType)
|
||||
|
|
|
@ -52,9 +52,9 @@ public:
|
|||
#endif
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
int VISUALISER_SETTINGS_HEIGHT = 1100;
|
||||
int VISUALISER_SETTINGS_HEIGHT = 1250;
|
||||
#else
|
||||
int VISUALISER_SETTINGS_HEIGHT = 700;
|
||||
int VISUALISER_SETTINGS_HEIGHT = 800;
|
||||
#endif
|
||||
|
||||
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters, 3);
|
||||
|
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE framework.
|
||||
Copyright (c) Raw Material Software Limited
|
||||
|
||||
JUCE is an open source framework subject to commercial or open source
|
||||
licensing.
|
||||
|
||||
By downloading, installing, or using the JUCE framework, or combining the
|
||||
JUCE framework with any other source code, object code, content or any other
|
||||
copyrightable work, you agree to the terms of the JUCE End User Licence
|
||||
Agreement, and all incorporated terms including the JUCE Privacy Policy and
|
||||
the JUCE Website Terms of Service, as applicable, which will bind you. If you
|
||||
do not agree to the terms of these agreements, we will not license the JUCE
|
||||
framework to you, and you must discontinue the installation or download
|
||||
process and cease use of the JUCE framework.
|
||||
|
||||
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
|
||||
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
|
||||
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
|
||||
|
||||
Or:
|
||||
|
||||
You may also use this code under the terms of the AGPLv3:
|
||||
https://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
|
||||
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
|
||||
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
|
||||
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include <juce_core/system/juce_TargetPlatform.h>
|
||||
|
||||
#if JucePlugin_Build_Standalone
|
||||
|
||||
#if ! JUCE_MODULE_AVAILABLE_juce_audio_utils
|
||||
#error To compile AudioUnitv3 and/or Standalone plug-ins, you need to add the juce_audio_utils and juce_audio_devices modules!
|
||||
#endif
|
||||
|
||||
#include <juce_core/system/juce_TargetPlatform.h>
|
||||
#include <juce_audio_plugin_client/detail/juce_CheckSettingMacros.h>
|
||||
|
||||
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h>
|
||||
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h>
|
||||
#include <juce_gui_basics/native/juce_WindowsHooks_windows.h>
|
||||
#include <juce_audio_plugin_client/detail/juce_PluginUtilities.h>
|
||||
|
||||
#include <juce_audio_devices/juce_audio_devices.h>
|
||||
#include <juce_gui_extra/juce_gui_extra.h>
|
||||
#include <juce_audio_utils/juce_audio_utils.h>
|
||||
|
||||
// You can set this flag in your build if you need to specify a different
|
||||
// standalone JUCEApplication class for your app to use. If you don't
|
||||
// set it then by default we'll just create a simple one as below.
|
||||
#if JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP
|
||||
|
||||
// #include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
|
||||
#include "CustomStandaloneFilterWindow.h"
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class CustomStandaloneFilterApp final : public JUCEApplication
|
||||
{
|
||||
public:
|
||||
CustomStandaloneFilterApp()
|
||||
{
|
||||
PropertiesFile::Options options;
|
||||
|
||||
options.applicationName = CharPointer_UTF8 (JucePlugin_Name);
|
||||
options.filenameSuffix = ".settings";
|
||||
options.osxLibrarySubFolder = "Application Support";
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
options.folderName = "~/.config";
|
||||
#else
|
||||
options.folderName = "";
|
||||
#endif
|
||||
|
||||
appProperties.setStorageParameters (options);
|
||||
}
|
||||
|
||||
const String getApplicationName() override { return CharPointer_UTF8 (JucePlugin_Name); }
|
||||
const String getApplicationVersion() override { return JucePlugin_VersionString; }
|
||||
bool moreThanOneInstanceAllowed() override { return true; }
|
||||
void anotherInstanceStarted (const String& commandLine) override
|
||||
{
|
||||
if (mainWindow != nullptr)
|
||||
{
|
||||
mainWindow->toFront(true);
|
||||
mainWindow->handleCommandLine(commandLine);
|
||||
}
|
||||
}
|
||||
|
||||
virtual StandaloneFilterWindow* createWindow()
|
||||
{
|
||||
if (Desktop::getInstance().getDisplays().displays.isEmpty())
|
||||
{
|
||||
// No displays are available, so no window will be created!
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new StandaloneFilterWindow (getApplicationName(),
|
||||
LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId),
|
||||
createPluginHolder());
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<StandalonePluginHolder> createPluginHolder()
|
||||
{
|
||||
constexpr auto autoOpenMidiDevices =
|
||||
#if (JUCE_ANDROID || JUCE_IOS) && ! JUCE_DONT_AUTO_OPEN_MIDI_DEVICES_ON_MOBILE
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef JucePlugin_PreferredChannelConfigurations
|
||||
constexpr StandalonePluginHolder::PluginInOuts channels[] { JucePlugin_PreferredChannelConfigurations };
|
||||
const Array<StandalonePluginHolder::PluginInOuts> channelConfig (channels, juce::numElementsInArray (channels));
|
||||
#else
|
||||
const Array<StandalonePluginHolder::PluginInOuts> channelConfig;
|
||||
#endif
|
||||
|
||||
return std::make_unique<StandalonePluginHolder> (appProperties.getUserSettings(),
|
||||
false,
|
||||
String{},
|
||||
nullptr,
|
||||
channelConfig,
|
||||
autoOpenMidiDevices);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const String& commandLine) override
|
||||
{
|
||||
mainWindow = rawToUniquePtr (createWindow());
|
||||
|
||||
if (mainWindow != nullptr)
|
||||
{
|
||||
#if JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE
|
||||
Desktop::getInstance().setKioskModeComponent (mainWindow.get(), false);
|
||||
#endif
|
||||
|
||||
mainWindow->setVisible (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
pluginHolder = createPluginHolder();
|
||||
mainWindow->handleCommandLine(commandLine);
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
pluginHolder = nullptr;
|
||||
mainWindow = nullptr;
|
||||
appProperties.saveIfNeeded();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
if (pluginHolder != nullptr)
|
||||
pluginHolder->savePluginState();
|
||||
|
||||
if (mainWindow != nullptr)
|
||||
mainWindow->pluginHolder->savePluginState();
|
||||
|
||||
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
|
||||
{
|
||||
Timer::callAfterDelay (100, []()
|
||||
{
|
||||
if (auto app = JUCEApplicationBase::getInstance())
|
||||
app->systemRequestedQuit();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
quit();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
ApplicationProperties appProperties;
|
||||
std::unique_ptr<StandaloneFilterWindow> mainWindow;
|
||||
|
||||
private:
|
||||
std::unique_ptr<StandalonePluginHolder> pluginHolder;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
||||
#if JucePlugin_Build_Standalone && JUCE_IOS
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes")
|
||||
|
||||
using namespace juce;
|
||||
|
||||
bool JUCE_CALLTYPE juce_isInterAppAudioConnected()
|
||||
{
|
||||
if (auto holder = StandalonePluginHolder::getInstance())
|
||||
return holder->isInterAppAudioConnected();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE juce_switchToHostApplication()
|
||||
{
|
||||
if (auto holder = StandalonePluginHolder::getInstance())
|
||||
holder->switchToHostApplication();
|
||||
}
|
||||
|
||||
Image JUCE_CALLTYPE juce_getIAAHostIcon (int size)
|
||||
{
|
||||
if (auto holder = StandalonePluginHolder::getInstance())
|
||||
return holder->getIAAHostIcon (size);
|
||||
|
||||
return Image();
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP
|
||||
// extern juce::JUCEApplicationBase* juce_CreateApplication();
|
||||
JUCE_CREATE_APPLICATION_DEFINE (juce::CustomStandaloneFilterApp)
|
||||
|
||||
#if JUCE_IOS
|
||||
extern void* juce_GetIOSCustomDelegateClass();
|
||||
#endif
|
||||
|
||||
#else
|
||||
JUCE_CREATE_APPLICATION_DEFINE (juce::CustomStandaloneFilterApp)
|
||||
#endif
|
||||
|
||||
#if ! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_ENTRYPOINT
|
||||
// JUCE_MAIN_FUNCTION_DEFINITION
|
||||
#endif
|
||||
|
||||
#endif
|
Plik diff jest za duży
Load Diff
|
@ -10,7 +10,7 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
fileButton.setButtonText("Choose File(s)");
|
||||
|
||||
fileButton.onClick = [this] {
|
||||
chooser = std::make_unique<juce::FileChooser>("Open", audioProcessor.lastOpenedDirectory, "*.obj;*.svg;*.lua;*.txt;*.gpla;*.gif;*.png;*.jpg;*.jpeg;*.wav;*.aiff");
|
||||
chooser = std::make_unique<juce::FileChooser>("Open", audioProcessor.lastOpenedDirectory, "*.obj;*.svg;*.lua;*.txt;*.gpla;*.gif;*.png;*.jpg;*.jpeg;*.wav;*.aiff;*.ogg;*.flac;*.mp3");
|
||||
auto flags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectMultipleItems |
|
||||
juce::FileBrowserComponent::canSelectFiles;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "PluginProcessor.h"
|
||||
#include "PluginEditor.h"
|
||||
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
|
||||
#include "CustomStandaloneFilterWindow.h"
|
||||
|
||||
OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioProcessor& p) : CommonPluginEditor(p, "osci-render", "osci", 1100, 750), audioProcessor(p), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white) {
|
||||
#if !SOSCI_FEATURES
|
||||
|
@ -109,6 +109,9 @@ bool OscirenderAudioProcessorEditor::isInterestedInFileDrag(const juce::StringAr
|
|||
return
|
||||
file.hasFileExtension("wav") ||
|
||||
file.hasFileExtension("aiff") ||
|
||||
file.hasFileExtension("ogg") ||
|
||||
file.hasFileExtension("flac") ||
|
||||
file.hasFileExtension("mp3") ||
|
||||
file.hasFileExtension("osci") ||
|
||||
file.hasFileExtension("txt") ||
|
||||
file.hasFileExtension("lua") ||
|
||||
|
@ -139,7 +142,8 @@ void OscirenderAudioProcessorEditor::filesDropped(const juce::StringArray& files
|
|||
}
|
||||
|
||||
bool OscirenderAudioProcessorEditor::isBinaryFile(juce::String name) {
|
||||
return name.endsWith(".gpla") || name.endsWith(".gif") || name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".wav") || name.endsWith(".aiff");
|
||||
name = name.toLowerCase();
|
||||
return name.endsWith(".gpla") || name.endsWith(".gif") || name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".wav") || name.endsWith(".aiff") || name.endsWith(".ogg") || name.endsWith(".mp3") || name.endsWith(".flac");
|
||||
}
|
||||
|
||||
// parsersLock must be held
|
||||
|
|
|
@ -321,7 +321,7 @@ void OscirenderAudioProcessor::openFile(int index) {
|
|||
if (index < 0 || index >= fileBlocks.size()) {
|
||||
return;
|
||||
}
|
||||
parsers[index]->parse(juce::String(fileIds[index]), fileNames[index].fromLastOccurrenceOf(".", true, false), std::make_unique<juce::MemoryInputStream>(*fileBlocks[index], false), font);
|
||||
parsers[index]->parse(juce::String(fileIds[index]), fileNames[index].fromLastOccurrenceOf(".", true, false).toLowerCase(), std::make_unique<juce::MemoryInputStream>(*fileBlocks[index], false), font);
|
||||
changeCurrentFile(index);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "SosciPluginProcessor.h"
|
||||
#include "SosciPluginEditor.h"
|
||||
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
|
||||
#include "CustomStandaloneFilterWindow.h"
|
||||
|
||||
SosciPluginEditor::SosciPluginEditor(SosciAudioProcessor& p) : CommonPluginEditor(p, "sosci", "sosci", 1180, 750), audioProcessor(p) {
|
||||
initialiseMenuBar(model);
|
||||
|
|
|
@ -192,7 +192,9 @@ void EffectComponent::parameterGestureChanged(int parameterIndex, bool gestureIs
|
|||
|
||||
void EffectComponent::handleAsyncUpdate() {
|
||||
setupComponent();
|
||||
getParentComponent()->repaint();
|
||||
if (auto* parent = getParentComponent()) {
|
||||
parent->repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void EffectComponent::setRangeEnabled(bool enabled) {
|
||||
|
|
|
@ -10,9 +10,9 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
|
|||
addTopLevelMenu("Audio");
|
||||
}
|
||||
|
||||
addMenuItem(0, "Open", [this] { editor.openProject(); });
|
||||
addMenuItem(0, "Save", [this] { editor.saveProject(); });
|
||||
addMenuItem(0, "Save As", [this] { editor.saveProjectAs(); });
|
||||
addMenuItem(0, "Open Project", [this] { editor.openProject(); });
|
||||
addMenuItem(0, "Save Project", [this] { editor.saveProject(); });
|
||||
addMenuItem(0, "Save Project As", [this] { editor.saveProjectAs(); });
|
||||
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
|
||||
addMenuItem(0, "Create New Project", [this] { editor.resetToDefault(); });
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ ImageParser::ImageParser(OscirenderAudioProcessor& p, juce::String extension, ju
|
|||
if (output.openedOk()) {
|
||||
output.write(image.getData(), image.getSize());
|
||||
output.flush();
|
||||
} else {
|
||||
handleError("The image could not be loaded.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,38 +46,45 @@ ImageParser::ImageParser(OscirenderAudioProcessor& p, juce::String extension, ju
|
|||
}
|
||||
|
||||
gd_close_gif(gif);
|
||||
} else {
|
||||
handleError("The image could not be loaded. Please try optimising the GIF with https://ezgif.com/optimize.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
juce::Image image = juce::ImageFileFormat::loadFrom(file);
|
||||
image.desaturate();
|
||||
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
int frameSize = width * height;
|
||||
|
||||
visited = std::vector<bool>(frameSize, false);
|
||||
frames.emplace_back(std::vector<uint8_t>(frameSize));
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
juce::Colour pixel = image.getPixelAt(x, y);
|
||||
int index = y * width + x;
|
||||
// RGB should be equal since we have desaturated
|
||||
int value = pixel.getRed();
|
||||
// value of 0 is reserved for transparent pixels
|
||||
frames[0][index] = pixel.isTransparent() ? 0 : juce::jmax(1, value);
|
||||
if (image.isValid()) {
|
||||
image.desaturate();
|
||||
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
int frameSize = width * height;
|
||||
|
||||
visited = std::vector<bool>(frameSize, false);
|
||||
frames.emplace_back(std::vector<uint8_t>(frameSize));
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
juce::Colour pixel = image.getPixelAt(x, y);
|
||||
int index = y * width + x;
|
||||
// RGB should be equal since we have desaturated
|
||||
int value = pixel.getRed();
|
||||
// value of 0 is reserved for transparent pixels
|
||||
frames[0][index] = pixel.isTransparent() ? 0 : juce::jmax(1, value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handleError("The image could not be loaded.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (frames.size() == 0) {
|
||||
juce::MessageManager::callAsync([this] {
|
||||
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon, "Invalid GIF", "The image could not be loaded. Please try optimising the GIF with https://ezgif.com/optimize.");
|
||||
});
|
||||
|
||||
width = 1;
|
||||
height = 1;
|
||||
frames.emplace_back(std::vector<uint8_t>(1));
|
||||
if (extension.equalsIgnoreCase(".gif")) {
|
||||
handleError("The image could not be loaded. Please try optimising the GIF with https://ezgif.com/optimize.");
|
||||
} else {
|
||||
handleError("The image could not be loaded.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setFrame(0);
|
||||
|
@ -82,6 +92,17 @@ ImageParser::ImageParser(OscirenderAudioProcessor& p, juce::String extension, ju
|
|||
|
||||
ImageParser::~ImageParser() {}
|
||||
|
||||
void ImageParser::handleError(juce::String message) {
|
||||
juce::MessageManager::callAsync([this, message] {
|
||||
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon, "Error", message);
|
||||
});
|
||||
|
||||
width = 1;
|
||||
height = 1;
|
||||
frames.emplace_back(std::vector<uint8_t>(1));
|
||||
setFrame(0);
|
||||
}
|
||||
|
||||
void ImageParser::setFrame(int index) {
|
||||
// Ensure that the frame number is within the bounds of the number of frames
|
||||
// This weird modulo trick is to handle negative numbers
|
||||
|
@ -160,20 +181,52 @@ void ImageParser::findNearestNeighbour(int searchRadius, float thresholdPow, int
|
|||
}
|
||||
|
||||
OsciPoint ImageParser::getSample() {
|
||||
if (count % jumpFrequency() == 0) {
|
||||
resetPosition();
|
||||
if (ALGORITHM == "HILLIGOSS") {
|
||||
if (count % jumpFrequency() == 0) {
|
||||
resetPosition();
|
||||
}
|
||||
|
||||
if (count % 10 * jumpFrequency() == 0) {
|
||||
std::fill(visited.begin(), visited.end(), false);
|
||||
}
|
||||
|
||||
float thresholdPow = audioProcessor.imageThreshold->getActualValue() * 10 + 1;
|
||||
|
||||
findNearestNeighbour(10, thresholdPow, audioProcessor.imageStride->getActualValue(), audioProcessor.invertImage->getValue());
|
||||
float maxDim = juce::jmax(width, height);
|
||||
count++;
|
||||
float widthDiff = (maxDim - width) / 2;
|
||||
float heightDiff = (maxDim - height) / 2;
|
||||
return OsciPoint(2 * (currentX + widthDiff) / maxDim - 1, 2 * (currentY + heightDiff) / maxDim - 1);
|
||||
} else {
|
||||
double scanIncrement = audioProcessor.imageStride->getActualValue() / 100;
|
||||
|
||||
double pixel = 0;
|
||||
int maxIterations = 10000;
|
||||
while (pixel <= audioProcessor.imageThreshold->getActualValue() && maxIterations > 0) {
|
||||
int x = (int) ((scanX + 1) * width / 2);
|
||||
int y = (int) ((scanY + 1) * height / 2);
|
||||
pixel = getPixelValue(x, y, audioProcessor.invertImage->getValue());
|
||||
|
||||
double increment = 0.01;
|
||||
if (pixel > audioProcessor.imageThreshold->getActualValue()) {
|
||||
increment = (1 - tanh(4 * pixel)) * 0.3;
|
||||
}
|
||||
|
||||
scanX += increment;
|
||||
if (scanX >= 1) {
|
||||
scanX = -1;
|
||||
scanY -= scanIncrement;
|
||||
}
|
||||
if (scanY < -1) {
|
||||
double offset = ((scanCount % 15) / 15.0) * scanIncrement;
|
||||
scanY = 1 - offset;
|
||||
scanCount++;
|
||||
}
|
||||
|
||||
maxIterations--;
|
||||
}
|
||||
|
||||
return OsciPoint(scanX, scanY);
|
||||
}
|
||||
|
||||
if (count % 10 * jumpFrequency() == 0) {
|
||||
std::fill(visited.begin(), visited.end(), false);
|
||||
}
|
||||
|
||||
float thresholdPow = audioProcessor.imageThreshold->getActualValue() * 10 + 1;
|
||||
|
||||
findNearestNeighbour(10, thresholdPow, audioProcessor.imageStride->getActualValue(), audioProcessor.invertImage->getValue());
|
||||
float maxDim = juce::jmax(width, height);
|
||||
count++;
|
||||
float widthDiff = (maxDim - width) / 2;
|
||||
float heightDiff = (maxDim - height) / 2;
|
||||
return OsciPoint(2 * (currentX + widthDiff) / maxDim - 1, 2 * (currentY + heightDiff) / maxDim - 1);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ private:
|
|||
void findWhite(double thresholdPow, bool invert);
|
||||
bool isOverThreshold(double pixel, double thresholdValue);
|
||||
int jumpFrequency();
|
||||
void handleError(juce::String message);
|
||||
|
||||
const juce::String ALGORITHM = "HILLIGOSS";
|
||||
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
juce::Random rng;
|
||||
|
@ -30,4 +33,9 @@ private:
|
|||
int currentX, currentY;
|
||||
int width, height;
|
||||
int count = 0;
|
||||
|
||||
// experiments
|
||||
double scanX = -1;
|
||||
double scanY = 1;
|
||||
int scanCount = 0;
|
||||
};
|
||||
|
|
|
@ -41,8 +41,7 @@ void FileParser::parse(juce::String fileId, juce::String extension, std::unique_
|
|||
}
|
||||
if (isBinary) {
|
||||
gpla = std::make_shared<LineArtParser>(gplaData, bytesRead);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
stream->setPosition(0);
|
||||
gpla = std::make_shared<LineArtParser>(stream->readEntireStreamAsString());
|
||||
}
|
||||
|
@ -50,9 +49,13 @@ void FileParser::parse(juce::String fileId, juce::String extension, std::unique_
|
|||
juce::MemoryBlock buffer{};
|
||||
int bytesRead = stream->readIntoMemoryBlock(buffer);
|
||||
img = std::make_shared<ImageParser>(audioProcessor, extension, buffer);
|
||||
} else if (extension == ".wav" || extension == ".aiff") {
|
||||
} else if (extension == ".wav" || extension == ".aiff" || extension == ".flac" || extension == ".ogg" || extension == ".mp3") {
|
||||
wav = std::make_shared<WavParser>(audioProcessor);
|
||||
wav->parse(std::move(stream));
|
||||
if (!wav->parse(std::move(stream))) {
|
||||
juce::MessageManager::callAsync([this] {
|
||||
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon, "Error", "The audio file could not be loaded.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isAnimatable = gpla != nullptr || (img != nullptr && extension == ".gif");
|
||||
|
|
|
@ -22,6 +22,10 @@ SvgParser::SvgParser(juce::String svgFile) {
|
|||
}
|
||||
}
|
||||
|
||||
juce::MessageManager::callAsync([this] {
|
||||
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon, "Error", "The SVG could not be loaded.");
|
||||
});
|
||||
|
||||
// draw an X to indicate an error.
|
||||
shapes.push_back(std::make_unique<Line>(-0.5, -0.5, 0.5, 0.5));
|
||||
shapes.push_back(std::make_unique<Line>(-0.5, 0.5, 0.5, -0.5));
|
||||
|
|
|
@ -9,7 +9,6 @@ uniform float uSize;
|
|||
uniform float uIntensity;
|
||||
uniform vec2 uOffset;
|
||||
uniform vec2 uScale;
|
||||
uniform float uScreenOverlay;
|
||||
uniform float uFishEye;
|
||||
uniform sampler2D uScreen;
|
||||
varying float vSize;
|
||||
|
|
|
@ -7,6 +7,7 @@ uniform float uSize;
|
|||
uniform float uNEdges;
|
||||
uniform float uFadeAmount;
|
||||
uniform float uIntensity;
|
||||
uniform bool uShutterSync;
|
||||
uniform float uGain;
|
||||
attribute vec3 aStart, aEnd;
|
||||
attribute float aIdx;
|
||||
|
@ -25,8 +26,8 @@ void main () {
|
|||
|
||||
vec2 aStartPos = aStart.xy;
|
||||
vec2 aEndPos = aEnd.xy;
|
||||
float aStartBrightness = aStart.z;
|
||||
float aEndBrightness = aEnd.z;
|
||||
float aStartBrightness = clamp(aStart.z, 0.0, 1.0);
|
||||
float aEndBrightness = clamp(aEnd.z, 0.0, 1.0);
|
||||
|
||||
// `dir` vector is storing the normalized difference
|
||||
// between end and start
|
||||
|
@ -56,17 +57,22 @@ void main () {
|
|||
uvl.w = aStartBrightness;
|
||||
}
|
||||
// `side` corresponds to shift to the "right" or "left"
|
||||
float side = (mod(idx, 2.0)-0.5)*2.0;
|
||||
float side = (mod(idx, 2.0) - 0.5) * 2.0;
|
||||
uvl.y = side * vSize;
|
||||
|
||||
uvl.w *= intensity * mix(1.0-uFadeAmount, 1.0, floor(aIdx / 4.0 + 0.5)/uNEdges);
|
||||
float intensityScale = floor(aIdx / 4.0 + 0.5)/uNEdges;
|
||||
|
||||
if (uShutterSync) {
|
||||
float avgIntensityScale = floor(uNEdges / 4.0 + 0.5)/uNEdges;
|
||||
intensityScale = avgIntensityScale;
|
||||
}
|
||||
float intensityFade = mix(1.0 - uFadeAmount, 1.0, intensityScale);
|
||||
|
||||
uvl.w *= intensity * intensityFade;
|
||||
|
||||
vec4 pos = vec4((current+(tang*dir+norm*side)*vSize)*uInvert,0.0,1.0);
|
||||
gl_Position = pos;
|
||||
vTexCoord = 0.5*pos.xy+0.5;
|
||||
//float seed = floor(aIdx/4.0);
|
||||
//seed = mod(sin(seed*seed), 7.0);
|
||||
//if (mod(seed/2.0, 1.0)<0.5) gl_Position = vec4(10.0);
|
||||
vTexCoord = 0.5 * pos.xy + 0.5;
|
||||
}
|
||||
|
||||
)";
|
||||
|
|
|
@ -65,7 +65,7 @@ void main() {
|
|||
// r components have grid; g components do not.
|
||||
vec4 screen = texture2D(uTexture3, vTexCoord);
|
||||
vec4 tightGlow = texture2D(uTexture1, linePos);
|
||||
vec4 scatter = texture2D(uTexture2, linePos) + (1.0 - uRealScreen) * max(uAmbient - 0.35, 0.0);
|
||||
vec4 scatter = texture2D(uTexture2, linePos);
|
||||
|
||||
if (uRealScreen > 0.5) {
|
||||
vec4 reflection = texture2D(uTexture4, vTexCoord);
|
||||
|
@ -73,8 +73,14 @@ void main() {
|
|||
scatter += max4(screenGlow * reflection * max(1.0 - 0.5 * uAmbient, 0.0), vec4(0.0));
|
||||
}
|
||||
|
||||
float light = line.r + uGlow * 1.5 * screen.g * screen.g * tightGlow.r;
|
||||
light += uGlow * 0.3 * scatter.g * (2.0 + 1.0 * screen.g + 0.5 * screen.r);
|
||||
// making the range of the glow slider more useful
|
||||
float glow = 1.05 * pow(uGlow, 1.5);
|
||||
float light = line.r + glow * 1.5 * screen.g * screen.g * tightGlow.r;
|
||||
float scatterScalar = 0.3 * (2.0 + 1.0 * screen.g + 0.5 * screen.r);
|
||||
light += glow * scatter.g * scatterScalar;
|
||||
// add ambient light to graticule
|
||||
light += (1.0 - uRealScreen) * max(uAmbient - 0.35, 0.0) * scatterScalar;
|
||||
|
||||
float tlight = 1.0-pow(2.0, -uExposure*light);
|
||||
float tlight2 = tlight * tlight * tlight;
|
||||
gl_FragColor.rgb = mix(uColour, vec3(1.0), 0.3+tlight2*tlight2*uOverexposure) * tlight;
|
||||
|
|
|
@ -239,6 +239,14 @@ void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
|||
for (const OsciPoint& rawPoint : points) {
|
||||
OsciPoint point = applyEffects(rawPoint);
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
if (settings.isGoniometer()) {
|
||||
// x and y go to a diagonal currently, so we need to scale them down, and rotate them
|
||||
point.scale(1.0 / std::sqrt(2.0), 1.0 / std::sqrt(2.0), 1.0);
|
||||
point.rotate(0, 0, juce::MathConstants<double>::pi / 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
xSamples.push_back(point.x);
|
||||
ySamples.push_back(point.y);
|
||||
zSamples.push_back(point.z);
|
||||
|
@ -1083,8 +1091,11 @@ void VisualiserComponent::drawLine(const std::vector<float>& xPoints, const std:
|
|||
setOffsetAndScale(lineShader.get());
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
lineShader->setUniform("uScreenOverlay", (GLfloat) screenOverlay);
|
||||
lineShader->setUniform("uFishEye", screenOverlay == ScreenOverlay::VectorDisplay ? VECTOR_DISPLAY_FISH_EYE : 0.0f);
|
||||
lineShader->setUniform("uShutterSync", settings.getShutterSync());
|
||||
#else
|
||||
lineShader->setUniform("uFishEye", 0.0f);
|
||||
lineShader->setUniform("uShutterSync", false);
|
||||
#endif
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
|
||||
|
|
|
@ -240,7 +240,7 @@ private:
|
|||
juce::OpenGLShaderProgram* currentShader;
|
||||
|
||||
float fadeAmount;
|
||||
ScreenOverlay screenOverlay = ScreenOverlay::MAX;
|
||||
ScreenOverlay screenOverlay = ScreenOverlay::INVALID;
|
||||
|
||||
const double RESAMPLE_RATIO = 6.0;
|
||||
double sampleRate = -1;
|
||||
|
|
|
@ -19,6 +19,8 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
|
|||
addAndMakeVisible(screenColour);
|
||||
addAndMakeVisible(flipVerticalToggle);
|
||||
addAndMakeVisible(flipHorizontalToggle);
|
||||
addAndMakeVisible(goniometerToggle);
|
||||
addAndMakeVisible(shutterSyncToggle);
|
||||
#endif
|
||||
|
||||
for (int i = 1; i <= parameters.screenOverlay->max; i++) {
|
||||
|
@ -81,6 +83,8 @@ void VisualiserSettings::resized() {
|
|||
area.removeFromTop(10);
|
||||
flipVerticalToggle.setBounds(area.removeFromTop(rowHeight));
|
||||
flipHorizontalToggle.setBounds(area.removeFromTop(rowHeight));
|
||||
goniometerToggle.setBounds(area.removeFromTop(rowHeight));
|
||||
shutterSyncToggle.setBounds(area.removeFromTop(rowHeight));
|
||||
#endif
|
||||
|
||||
#if !SOSCI_FEATURES
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "../audio/StereoEffect.h"
|
||||
|
||||
enum class ScreenOverlay : int {
|
||||
INVALID = -1,
|
||||
Empty = 1,
|
||||
Graticule = 2,
|
||||
Smudged = 3,
|
||||
|
@ -97,6 +98,8 @@ public:
|
|||
#if SOSCI_FEATURES
|
||||
BooleanParameter* flipVertical = new BooleanParameter("Flip Vertical", "flipVertical", VERSION_HINT, false, "Flips the visualiser vertically.");
|
||||
BooleanParameter* flipHorizontal = new BooleanParameter("Flip Horizontal", "flipHorizontal", VERSION_HINT, false, "Flips the visualiser horizontally.");
|
||||
BooleanParameter* goniometer = new BooleanParameter("Goniometer", "goniometer", VERSION_HINT, false, "Rotates the visualiser to replicate a goniometer display to show the phase relationship between two channels.");
|
||||
BooleanParameter* shutterSync = new BooleanParameter("Shutter Sync", "shutterSync", VERSION_HINT, false, "Controls whether the camera's shutter speed is in sync with framerate. This makes the brightness of a single frame constant. This can be beneficial when the drawing frequency and frame rate are in sync.");
|
||||
|
||||
std::shared_ptr<Effect> screenSaturationEffect = std::make_shared<Effect>(
|
||||
new EffectParameter(
|
||||
|
@ -301,6 +304,8 @@ public:
|
|||
#if SOSCI_FEATURES
|
||||
flipVertical,
|
||||
flipHorizontal,
|
||||
goniometer,
|
||||
shutterSync,
|
||||
#endif
|
||||
};
|
||||
std::vector<IntParameter*> integers = {
|
||||
|
@ -389,6 +394,14 @@ public:
|
|||
bool isFlippedHorizontal() {
|
||||
return parameters.flipHorizontal->getBoolValue();
|
||||
}
|
||||
|
||||
bool isGoniometer() {
|
||||
return parameters.goniometer->getBoolValue();
|
||||
}
|
||||
|
||||
bool getShutterSync() {
|
||||
return parameters.shutterSync->getBoolValue();
|
||||
}
|
||||
#endif
|
||||
|
||||
double getFocus() {
|
||||
|
@ -505,6 +518,8 @@ private:
|
|||
|
||||
jux::SwitchButton flipVerticalToggle{parameters.flipVertical};
|
||||
jux::SwitchButton flipHorizontalToggle{parameters.flipHorizontal};
|
||||
jux::SwitchButton goniometerToggle{parameters.goniometer};
|
||||
jux::SwitchButton shutterSyncToggle{parameters.shutterSync};
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserSettings)
|
||||
|
|
24
ci/build.sh
24
ci/build.sh
|
@ -23,17 +23,6 @@ eval "$RESAVE_COMMAND"
|
|||
if [ "$OS" = "mac" ]; then
|
||||
cd "$ROOT/Builds/$PLUGIN/MacOSX"
|
||||
xcodebuild -configuration Release || exit 1
|
||||
|
||||
cp -R "$ROOT/Builds/$PLUGIN/MacOSX/build/Release/$PLUGIN.app" "$ROOT/ci/bin/$OUTPUT_NAME.app"
|
||||
cp -R ~/Library/Audio/Plug-Ins/VST3/$PLUGIN.vst3 "$ROOT/ci/bin/$OUTPUT_NAME.vst3"
|
||||
cp -R ~/Library/Audio/Plug-Ins/Components/$PLUGIN.component "$ROOT/ci/bin/$OUTPUT_NAME.component"
|
||||
|
||||
cd "$ROOT/ci/bin"
|
||||
|
||||
zip -r ${OUTPUT_NAME}-mac.vst3.zip $OUTPUT_NAME.vst3
|
||||
zip -r ${OUTPUT_NAME}-mac.component.zip $OUTPUT_NAME.component
|
||||
zip -r ${OUTPUT_NAME}-mac.app.zip $OUTPUT_NAME.app
|
||||
cp ${OUTPUT_NAME}*.zip "$ROOT/bin"
|
||||
fi
|
||||
|
||||
# Build linux version
|
||||
|
@ -41,13 +30,13 @@ if [ "$OS" = "linux" ]; then
|
|||
cd "$ROOT/Builds/$PLUGIN/LinuxMakefile"
|
||||
make CONFIG=Release
|
||||
|
||||
cp -r ./build/$PLUGIN.vst3 "$ROOT/ci/bin/$OUTPUT_NAME.vst3"
|
||||
cp -r ./build/$PLUGIN "$ROOT/ci/bin/$OUTPUT_NAME"
|
||||
cp -r ./build/$PLUGIN.vst3 "$ROOT/ci/bin/$PLUGIN.vst3"
|
||||
cp -r ./build/$PLUGIN "$ROOT/ci/bin/$PLUGIN"
|
||||
|
||||
cd "$ROOT/ci/bin"
|
||||
|
||||
zip -r ${OUTPUT_NAME}-linux-vst3.zip $OUTPUT_NAME.vst3
|
||||
zip -r ${OUTPUT_NAME}-linux.zip $OUTPUT_NAME
|
||||
zip -r ${OUTPUT_NAME}-linux-vst3.zip $PLUGIN.vst3
|
||||
zip -r ${OUTPUT_NAME}-linux.zip $PLUGIN
|
||||
cp ${OUTPUT_NAME}*.zip "$ROOT/bin"
|
||||
fi
|
||||
|
||||
|
@ -60,10 +49,7 @@ if [ "$OS" = "win" ]; then
|
|||
|
||||
cd "$ROOT/Builds/$PLUGIN/VisualStudio2022"
|
||||
"$MSBUILD_EXE" "$PLUGIN.sln" "//p:VisualStudioVersion=16.0" "//m" "//t:Build" "//p:Configuration=Release" "//p:Platform=x64" "//p:PreferredToolArchitecture=x64" "//restore" "//p:RestorePackagesConfig=true"
|
||||
cd "$ROOT/ci/bin"
|
||||
cp "$ROOT/Builds/$PLUGIN/VisualStudio2022/x64/Release/Standalone Plugin/$PLUGIN.exe" "$ROOT/bin/$OUTPUT_NAME-win.exe"
|
||||
cp "$ROOT/Builds/$PLUGIN/VisualStudio2022/x64/Release/Standalone Plugin/$PLUGIN.pdb" "$ROOT/bin/$OUTPUT_NAME-win.pdb"
|
||||
cp -r "$ROOT/Builds/$PLUGIN/VisualStudio2022/x64/Release/VST3/$PLUGIN.vst3/Contents/x86_64-win/$PLUGIN.vst3" "$ROOT/bin/$OUTPUT_NAME-win.vst3"
|
||||
cp "$ROOT/Builds/$PLUGIN/VisualStudio2022/x64/Release/Standalone Plugin/$PLUGIN.pdb" "$ROOT/bin/$OUTPUT_NAME.pdb"
|
||||
fi
|
||||
|
||||
cd "$ROOT"
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginWantsMidiIn"
|
||||
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
|
||||
cppLanguageStandard="20" projectLineFeed=" " headerPath="./include"
|
||||
version="2.4.8.0" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
companyEmail="james@ball.sh" defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0 SOSCI_FEATURES=1"
|
||||
version="2.4.10.3" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
companyEmail="james@ball.sh" defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0 SOSCI_FEATURES=1 JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1"
|
||||
pluginAUMainType="'aumf'">
|
||||
<MAINGROUP id="j5Ge2T" name="osci-render">
|
||||
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
|
||||
|
@ -673,6 +673,10 @@
|
|||
file="Source/CommonPluginProcessor.cpp"/>
|
||||
<FILE id="PGeV6d" name="CommonPluginProcessor.h" compile="0" resource="0"
|
||||
file="Source/CommonPluginProcessor.h"/>
|
||||
<FILE id="TPGMru" name="CustomStandalone.cpp" compile="1" resource="0"
|
||||
file="Source/CustomStandalone.cpp"/>
|
||||
<FILE id="J0sbNw" name="CustomStandaloneFilterWindow.h" compile="0"
|
||||
resource="0" file="Source/CustomStandaloneFilterWindow.h"/>
|
||||
<FILE id="I44EdJ" name="EffectsComponent.cpp" compile="1" resource="0"
|
||||
file="Source/EffectsComponent.cpp"/>
|
||||
<FILE id="qxxNX3" name="EffectsComponent.h" compile="0" resource="0"
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<JUCERPROJECT id="HH2E72" name="sosci" projectType="audioplug" useAppConfig="0"
|
||||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginManufacturer="jameshball"
|
||||
aaxIdentifier="sh.ball.sosci" cppLanguageStandard="20" projectLineFeed=" "
|
||||
headerPath="./include" version="1.1.2.0" companyName="James H Ball"
|
||||
headerPath="./include" version="1.1.4.3" companyName="James H Ball"
|
||||
companyWebsite="https://osci-render.com" companyEmail="james@ball.sh"
|
||||
defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0 SOSCI_FEATURES=1"
|
||||
defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0 SOSCI_FEATURES=1 JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1"
|
||||
pluginManufacturerCode="Jhba" pluginCode="Sosc" pluginAUMainType="'aufx'">
|
||||
<MAINGROUP id="j5Ge2T" name="sosci">
|
||||
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
|
||||
|
@ -76,6 +76,10 @@
|
|||
</GROUP>
|
||||
</GROUP>
|
||||
<GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source">
|
||||
<FILE id="fqqP0r" name="CustomStandalone.cpp" compile="1" resource="0"
|
||||
file="Source/CustomStandalone.cpp"/>
|
||||
<FILE id="TFmWW0" name="CustomStandaloneFilterWindow.h" compile="0"
|
||||
resource="0" file="Source/CustomStandaloneFilterWindow.h"/>
|
||||
<GROUP id="{C63A0AA5-8550-16AC-EE89-C05416216534}" name="wav">
|
||||
<FILE id="jxAiTf" name="WavParser.cpp" compile="1" resource="0" file="Source/wav/WavParser.cpp"/>
|
||||
<FILE id="Q6iTsL" name="WavParser.h" compile="0" resource="0" file="Source/wav/WavParser.h"/>
|
||||
|
|
Ładowanie…
Reference in New Issue