Resolve conflicts

pull/218/head
James Ball 2024-01-30 19:29:31 +00:00
commit bfc70d71dc
15 zmienionych plików z 450 dodań i 15 usunięć

Wyświetl plik

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9Z" /></svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 222 B

Wyświetl plik

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6,2H18V8H18V8L14,12L18,16V16H18V22H6V16H6V16L10,12L6,8V8H6V2M16,16.5L12,12.5L8,16.5V20H16V16.5M12,11.5L16,7.5V4H8V7.5L12,11.5M10,6H14V6.75L12,8.75L10,6.75V6Z" /></svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 237 B

Wyświetl plik

@ -130,6 +130,8 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
frequencyLabel.setText(juce::String(roundedFrequency) + "Hz", juce::dontSendNotification); frequencyLabel.setText(juce::String(roundedFrequency) + "Hz", juce::dontSendNotification);
} }
); );
addAndMakeVisible(recorder);
} }
MainComponent::~MainComponent() { MainComponent::~MainComponent() {
@ -152,6 +154,9 @@ void MainComponent::resized() {
auto buttonHeight = 30; auto buttonHeight = 30;
auto padding = 10; auto padding = 10;
auto rowPadding = 10; auto rowPadding = 10;
recorder.setBounds(bounds.removeFromBottom(30));
bounds.removeFromBottom(padding);
auto row = bounds.removeFromTop(buttonHeight); auto row = bounds.removeFromTop(buttonHeight);
fileButton.setBounds(row.removeFromLeft(buttonWidth)); fileButton.setBounds(row.removeFromLeft(buttonWidth));

Wyświetl plik

@ -8,6 +8,7 @@
#include "audio/PitchDetector.h" #include "audio/PitchDetector.h"
#include "UGen/ugen_JuceEnvelopeComponent.h" #include "UGen/ugen_JuceEnvelopeComponent.h"
#include "components/SvgButton.h" #include "components/SvgButton.h"
#include "components/AudioRecordingComponent.h"
class OscirenderAudioProcessorEditor; class OscirenderAudioProcessorEditor;
class MainComponent : public juce::GroupComponent { class MainComponent : public juce::GroupComponent {
@ -25,7 +26,7 @@ private:
std::unique_ptr<juce::FileChooser> chooser; std::unique_ptr<juce::FileChooser> chooser;
juce::TextButton fileButton; juce::TextButton fileButton;
juce::TextButton closeFileButton; juce::TextButton closeFileButton;
SvgButton inputEnabled{"inputEnabled", juce::String(BinaryData::microphone_svg), "white", "red", audioProcessor.inputEnabled}; SvgButton inputEnabled{"inputEnabled", juce::String(BinaryData::microphone_svg), juce::Colours::white, juce::Colours::red, audioProcessor.inputEnabled};
juce::Label fileLabel; juce::Label fileLabel;
juce::TextEditor fileName; juce::TextEditor fileName;
@ -37,5 +38,7 @@ private:
juce::Label frequencyLabel; juce::Label frequencyLabel;
int callbackIndex = -1; int callbackIndex = -1;
AudioRecordingComponent recorder{audioProcessor};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
}; };

Wyświetl plik

@ -28,10 +28,10 @@ private:
juce::TextButton resetRotation{"Reset Rotation"}; juce::TextButton resetRotation{"Reset Rotation"};
juce::ToggleButton mouseRotate{"Rotate with Mouse (Esc to disable)"}; juce::ToggleButton mouseRotate{"Rotate with Mouse (Esc to disable)"};
std::shared_ptr<SvgButton> fixedRotateX = std::make_shared<SvgButton>("fixedRotateX", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.perspectiveEffect->fixedRotateX); std::shared_ptr<SvgButton> fixedRotateX = std::make_shared<SvgButton>("fixedRotateX", juce::String(BinaryData::fixed_rotate_svg), juce::Colours::white, juce::Colours::red, audioProcessor.perspectiveEffect->fixedRotateX);
std::shared_ptr<SvgButton> fixedRotateY = std::make_shared<SvgButton>("fixedRotateY", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.perspectiveEffect->fixedRotateY); std::shared_ptr<SvgButton> fixedRotateY = std::make_shared<SvgButton>("fixedRotateY", juce::String(BinaryData::fixed_rotate_svg), juce::Colours::white, juce::Colours::red, audioProcessor.perspectiveEffect->fixedRotateY);
std::shared_ptr<SvgButton> fixedRotateZ = std::make_shared<SvgButton>("fixedRotateZ", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.perspectiveEffect->fixedRotateZ); std::shared_ptr<SvgButton> fixedRotateZ = std::make_shared<SvgButton>("fixedRotateZ", juce::String(BinaryData::fixed_rotate_svg), juce::Colours::white, juce::Colours::red, audioProcessor.perspectiveEffect->fixedRotateZ);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PerspectiveComponent) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PerspectiveComponent)
}; };

Wyświetl plik

@ -168,6 +168,11 @@ const juce::String OscirenderAudioProcessor::getName() const {
return JucePlugin_Name; return JucePlugin_Name;
} }
void OscirenderAudioProcessor::setAudioThreadCallback(std::function<void(const juce::AudioBuffer<float>&)> callback) {
juce::SpinLock::ScopedLockType lock(audioThreadCallbackLock);
audioThreadCallback = callback;
}
bool OscirenderAudioProcessor::acceptsMidi() const { bool OscirenderAudioProcessor::acceptsMidi() const {
#if JucePlugin_WantsMidiInput #if JucePlugin_WantsMidiInput
return true; return true;
@ -631,6 +636,10 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
} }
} }
} }
// used for any callback that must guarantee all audio is recieved (e.g. when recording to a file)
juce::SpinLock::ScopedLockType lock(audioThreadCallbackLock);
audioThreadCallback(buffer);
} }
//============================================================================== //==============================================================================

Wyświetl plik

@ -52,6 +52,8 @@ public:
const juce::String getName() const override; const juce::String getName() const override;
void setAudioThreadCallback(std::function<void(const juce::AudioBuffer<float>&)> callback);
bool acceptsMidi() const override; bool acceptsMidi() const override;
bool producesMidi() const override; bool producesMidi() const override;
bool isMidiEffect() const override; bool isMidiEffect() const override;
@ -237,6 +239,9 @@ private:
bool prevMidiEnabled = !midiEnabled->getBoolValue(); bool prevMidiEnabled = !midiEnabled->getBoolValue();
juce::SpinLock audioThreadCallbackLock;
std::function<void(const juce::AudioBuffer<float>&)> audioThreadCallback;
std::vector<BooleanParameter*> booleanParameters; std::vector<BooleanParameter*> booleanParameters;
std::vector<FloatParameter*> floatParameters; std::vector<FloatParameter*> floatParameters;
std::vector<IntParameter*> intParameters; std::vector<IntParameter*> intParameters;

Wyświetl plik

@ -0,0 +1,321 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioRecordingComponent
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Records audio to a file.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioRecordingComponent
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "DoubleTextBox.h"
//==============================================================================
class AudioRecorder final {
public:
AudioRecorder(OscirenderAudioProcessor& p, juce::AudioThumbnail& thumbnailToUpdate)
: audioProcessor(p), thumbnail(thumbnailToUpdate) {
backgroundThread.startThread();
audioProcessor.setAudioThreadCallback([this](const juce::AudioBuffer<float>& buffer) { audioThreadCallback(buffer); });
}
~AudioRecorder() {
audioProcessor.setAudioThreadCallback(nullptr);
stop();
}
//==============================================================================
void startRecording(const juce::File& file) {
stop();
if (audioProcessor.currentSampleRate > 0) {
// Create an OutputStream to write to our destination file...
file.deleteFile();
if (auto fileStream = std::unique_ptr<juce::FileOutputStream>(file.createOutputStream())) {
// Now create a WAV writer object that writes to our output stream...
juce::WavAudioFormat wavFormat;
if (auto writer = wavFormat.createWriterFor(fileStream.get(), audioProcessor.currentSampleRate, 2, 32, {}, 0)) {
fileStream.release(); // (passes responsibility for deleting the stream to the writer object that is now using it)
// Now we'll create one of these helper objects which will act as a FIFO buffer, and will
// write the data to disk on our background thread.
threadedWriter.reset(new juce::AudioFormatWriter::ThreadedWriter(writer, backgroundThread, 32768));
// Reset our recording thumbnail
thumbnail.reset(writer->getNumChannels(), writer->getSampleRate());
nextSampleNum = 0;
// And now, swap over our active writer pointer so that the audio callback will start using it..
const juce::ScopedLock sl(writerLock);
activeWriter = threadedWriter.get();
}
}
}
}
void stop() {
// First, clear this pointer to stop the audio callback from using our writer object..
{
const juce::ScopedLock sl(writerLock);
activeWriter = nullptr;
}
// Now we can delete the writer object. It's done in this order because the deletion could
// take a little time while remaining data gets flushed to disk, so it's best to avoid blocking
// the audio callback while this happens.
threadedWriter.reset();
}
bool isRecording() const {
return activeWriter.load() != nullptr;
}
void audioThreadCallback(const juce::AudioBuffer<float>& buffer) {
if (nextSampleNum >= recordingLength * audioProcessor.currentSampleRate) {
stop();
stopCallback();
return;
}
const juce::ScopedLock sl(writerLock);
int numSamples = buffer.getNumSamples();
if (activeWriter.load() != nullptr) {
activeWriter.load()->write(buffer.getArrayOfReadPointers(), numSamples);
thumbnail.addBlock(nextSampleNum, buffer, 0, numSamples);
nextSampleNum += numSamples;
}
}
void setRecordLength(double recordLength) {
recordingLength = recordLength;
}
std::function<void()> stopCallback;
private:
OscirenderAudioProcessor& audioProcessor;
juce::AudioThumbnail& thumbnail;
juce::TimeSliceThread backgroundThread { "Audio Recorder Thread" }; // the thread that will write our audio data to disk
std::unique_ptr<juce::AudioFormatWriter::ThreadedWriter> threadedWriter; // the FIFO used to buffer the incoming data
juce::int64 nextSampleNum = 0;
double recordingLength = 99999999999.0;
juce::CriticalSection writerLock;
std::atomic<juce::AudioFormatWriter::ThreadedWriter*> activeWriter { nullptr };
};
//==============================================================================
class RecordingThumbnail final : public juce::Component,
private juce::ChangeListener {
public:
RecordingThumbnail() {
formatManager.registerBasicFormats();
thumbnail.addChangeListener(this);
}
~RecordingThumbnail() override {
thumbnail.removeChangeListener(this);
}
juce::AudioThumbnail& getAudioThumbnail() { return thumbnail; }
void setDisplayFullThumbnail(bool displayFull) {
displayFullThumb = displayFull;
repaint();
}
void paint(juce::Graphics& g) override {
g.setColour(juce::Colours::white);
if (thumbnail.getTotalLength() > 0.0) {
auto endTime = displayFullThumb ? thumbnail.getTotalLength()
: juce::jmax(30.0, thumbnail.getTotalLength());
auto thumbArea = getLocalBounds();
thumbnail.drawChannels(g, thumbArea.reduced(2), 0.0, endTime, 1.0f);
}
}
private:
juce::AudioFormatManager formatManager;
juce::AudioThumbnailCache thumbnailCache { 10 };
juce::AudioThumbnail thumbnail { 128, formatManager, thumbnailCache };
bool displayFullThumb = false;
void changeListenerCallback(juce::ChangeBroadcaster* source) override {
if (source == &thumbnail)
repaint();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RecordingThumbnail)
};
//==============================================================================
class AudioRecordingComponent final : public juce::Component {
public:
AudioRecordingComponent(OscirenderAudioProcessor& p) : audioProcessor(p) {
addAndMakeVisible(recordButton);
addAndMakeVisible(timedRecord);
addAndMakeVisible(recordLength);
recordButton.setTooltip("Start recording audio to a WAV file. Press again to stop and save the recording.");
timedRecord.setTooltip("Record for a set amount of time in seconds. When enabled, the recording will automatically stop once the time is reached.");
recordLength.setValue(1);
recordButton.onClick = [this] {
if (recordButton.getToggleState()) {
startRecording();
} else {
stopRecording();
}
};
timedRecord.onClick = [this] {
if (timedRecord.getToggleState()) {
addAndMakeVisible(recordLength);
} else {
removeChildComponent(&recordLength);
}
resized();
};
recorder.stopCallback = [this] {
juce::MessageManager::callAsync([this] {
recordButton.setToggleState(false, juce::sendNotification);
});
};
addAndMakeVisible(recordingThumbnail);
recordingThumbnail.setDisplayFullThumbnail(true);
}
void resized() override {
double iconSize = 25;
auto area = getLocalBounds();
recordButton.setBounds(area.removeFromLeft(iconSize).withSizeKeepingCentre(iconSize, iconSize));
area.removeFromLeft(5);
timedRecord.setBounds(area.removeFromLeft(iconSize).withSizeKeepingCentre(iconSize, iconSize));
if (timedRecord.getToggleState()) {
recordLength.setBounds(area.removeFromLeft(80).withSizeKeepingCentre(60, 25));
}
area.removeFromLeft(5);
recordingThumbnail.setBounds(area);
}
private:
OscirenderAudioProcessor& audioProcessor;
RecordingThumbnail recordingThumbnail;
AudioRecorder recorder{ audioProcessor, recordingThumbnail.getAudioThumbnail() };
SvgButton recordButton{ "record", BinaryData::record_svg, juce::Colours::white, juce::Colours::red };
juce::File lastRecording;
juce::FileChooser chooser { "Output file...", juce::File::getCurrentWorkingDirectory().getChildFile("recording.wav"), "*.wav" };
SvgButton timedRecord{ "timedRecord", BinaryData::timer_svg, juce::Colours::white, juce::Colours::red };
DoubleTextBox recordLength{ 0, 60 * 60 * 24 };
void startRecording() {
auto parentDir = juce::File::getSpecialLocation(juce::File::tempDirectory);
lastRecording = parentDir.getNonexistentChildFile("osci-render-recording", ".wav");
if (timedRecord.getToggleState()) {
recorder.setRecordLength(recordLength.getValue());
} else {
recorder.setRecordLength(99999999999.0);
}
recorder.startRecording(lastRecording);
recordButton.setColour(juce::TextButton::buttonColourId, juce::Colours::red);
recordButton.setColour(juce::TextButton::textColourOnId, juce::Colours::black);
}
void stopRecording() {
recorder.stop();
recordButton.setColour(juce::TextButton::buttonColourId, findColour(juce::TextButton::buttonColourId));
recordButton.setColour(juce::TextButton::textColourOnId, findColour(juce::TextButton::textColourOnId));
chooser.launchAsync(juce::FileBrowserComponent::saveMode
| juce::FileBrowserComponent::canSelectFiles
| juce::FileBrowserComponent::warnAboutOverwriting,
[this](const juce::FileChooser& c) {
if (juce::FileInputStream inputStream(lastRecording); inputStream.openedOk()) {
if (const auto outputStream = c.getURLResult().getLocalFile().createOutputStream()) {
outputStream->setPosition(0);
outputStream->truncate();
outputStream->writeFromInputStream(inputStream, -1);
}
}
lastRecording.deleteFile();
});
}
inline juce::Colour getUIColourIfAvailable(juce::LookAndFeel_V4::ColourScheme::UIColour uiColour, juce::Colour fallback = juce::Colour(0xff4d4d4d)) noexcept {
if (auto* v4 = dynamic_cast<juce::LookAndFeel_V4*> (&juce::LookAndFeel::getDefaultLookAndFeel()))
return v4->getCurrentColourScheme().getUIColour(uiColour);
return fallback;
}
inline std::unique_ptr<juce::OutputStream> makeOutputStream(const juce::URL& url) {
if (const auto doc = juce::AndroidDocument::fromDocument(url))
return doc.createOutputStream();
#if ! JUCE_IOS
if (url.isLocalFile())
return url.getLocalFile().createOutputStream();
#endif
return url.createOutputStream();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioRecordingComponent)
};

Wyświetl plik

@ -0,0 +1,59 @@
#pragma once
#include <JuceHeader.h>
class DoubleTextBox : public juce::TextEditor {
public:
DoubleTextBox(double minValue, double maxValue) : minValue(minValue), maxValue(maxValue) {
setText(juce::String(minValue, 2), false);
setMultiLine(false);
setJustification(juce::Justification::centred);
setFont(juce::Font(15.0f, juce::Font::plain));
onTextChange = [this]() {
setText(getText(), false);
};
}
double getValue() {
return getText().getDoubleValue();
}
void setValue(double value, bool sendChangeMessage = true) {
setText(juce::String(value, 2), sendChangeMessage);
}
void setText(const juce::String& newText, bool sendChangeMessage = true) {
// remove all non-digits
juce::String text = newText.retainCharacters("0123456789.-");
// only keep first decimal point
int firstDecimal = text.indexOfChar('.');
if (firstDecimal != -1) {
juce::String remainder = text.substring(firstDecimal + 1);
remainder = remainder.retainCharacters("0123456789");
text = text.substring(0, firstDecimal + 1) + remainder;
}
// only keep negative sign at beginning
if (text.contains("-")) {
juce::String remainder = text.substring(1);
remainder = remainder.retainCharacters("0123456789");
text = text.substring(0, 1) + remainder;
}
double value = text.getDoubleValue();
if (value < minValue || value > maxValue) {
text = juce::String(prevValue);
} else {
prevValue = value;
}
juce::TextEditor::setText(text, sendChangeMessage);
}
~DoubleTextBox() override {}
private:
double minValue;
double maxValue;
double prevValue = minValue;
};

Wyświetl plik

@ -9,7 +9,7 @@ EffectComponent::EffectComponent(OscirenderAudioProcessor& p, Effect& effect, in
sidechainEnabled = effect.parameters[0]->sidechain != nullptr; sidechainEnabled = effect.parameters[0]->sidechain != nullptr;
if (sidechainEnabled) { if (sidechainEnabled) {
sidechainButton = std::make_unique<SvgButton>(effect.parameters[0]->name, BinaryData::microphone_svg, "white", "red", effect.parameters[0]->sidechain); sidechainButton = std::make_unique<SvgButton>(effect.parameters[0]->name, BinaryData::microphone_svg, juce::Colours::white, juce::Colours::red, effect.parameters[0]->sidechain);
sidechainButton->setTooltip("When enabled, the volume of the input audio controls the value of the slider, acting like a sidechain effect."); sidechainButton->setTooltip("When enabled, the volume of the input audio controls the value of the slider, acting like a sidechain effect.");
addAndMakeVisible(*sidechainButton); addAndMakeVisible(*sidechainButton);
} }

Wyświetl plik

@ -97,14 +97,14 @@ std::shared_ptr<juce::Component> EffectsListComponent::createComponent(EffectPar
toggle = audioProcessor.perspectiveEffect->fixedRotateZ; toggle = audioProcessor.perspectiveEffect->fixedRotateZ;
axis = "Z"; axis = "Z";
} }
std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::fixed_rotate_svg, "white", "red", toggle); std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::fixed_rotate_svg, juce::Colours::white, juce::Colours::red, toggle);
button->setTooltip("Toggles whether the rotation around the " + axis + " axis is fixed, or changes according to the rotation speed."); button->setTooltip("Toggles whether the rotation around the " + axis + " axis is fixed, or changes according to the rotation speed.");
button->onClick = [this, toggle] { button->onClick = [this, toggle] {
toggle->setBoolValueNotifyingHost(!toggle->getBoolValue()); toggle->setBoolValueNotifyingHost(!toggle->getBoolValue());
}; };
return button; return button;
} else if (parameter->paramID == "customEffectStrength") { } else if (parameter->paramID == "customEffectStrength") {
std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::pencil_svg, "white", "red"); std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::pencil_svg, juce::Colours::white, juce::Colours::red);
std::weak_ptr<SvgButton> weakButton = button; std::weak_ptr<SvgButton> weakButton = button;
button->setEdgeIndent(5); button->setEdgeIndent(5);
button->setToggleState(editor.editingCustomFunction, juce::dontSendNotification); button->setToggleState(editor.editingCustomFunction, juce::dontSendNotification);

Wyświetl plik

@ -3,19 +3,33 @@
class SvgButton : public juce::DrawableButton, public juce::AudioProcessorParameter::Listener, public juce::AsyncUpdater { class SvgButton : public juce::DrawableButton, public juce::AudioProcessorParameter::Listener, public juce::AsyncUpdater {
public: public:
SvgButton(juce::String name, juce::String svg, juce::String colour, juce::String colourOn, BooleanParameter* toggle = nullptr) : juce::DrawableButton(name, juce::DrawableButton::ButtonStyle::ImageFitted), toggle(toggle) { SvgButton(juce::String name, juce::String svg, juce::Colour colour, juce::Colour colourOn, BooleanParameter* toggle = nullptr) : juce::DrawableButton(name, juce::DrawableButton::ButtonStyle::ImageFitted), toggle(toggle) {
auto doc = juce::XmlDocument::parse(svg); auto doc = juce::XmlDocument::parse(svg);
changeSvgColour(doc.get(), colour); changeSvgColour(doc.get(), colour);
normalImage = juce::Drawable::createFromSVG(*doc); normalImage = juce::Drawable::createFromSVG(*doc);
changeSvgColour(doc.get(), colour.withBrightness(0.7f));
overImage = juce::Drawable::createFromSVG(*doc);
changeSvgColour(doc.get(), colour.withBrightness(0.5f));
downImage = juce::Drawable::createFromSVG(*doc);
changeSvgColour(doc.get(), colour.withBrightness(0.3f));
disabledImage = juce::Drawable::createFromSVG(*doc);
changeSvgColour(doc.get(), colourOn); changeSvgColour(doc.get(), colourOn);
normalImageOn = juce::Drawable::createFromSVG(*doc); normalImageOn = juce::Drawable::createFromSVG(*doc);
changeSvgColour(doc.get(), colourOn.withBrightness(0.7f));
overImageOn = juce::Drawable::createFromSVG(*doc);
changeSvgColour(doc.get(), colourOn.withBrightness(0.5f));
downImageOn = juce::Drawable::createFromSVG(*doc);
changeSvgColour(doc.get(), colourOn.withBrightness(0.3f));
disabledImageOn = juce::Drawable::createFromSVG(*doc);
getLookAndFeel().setColour(juce::DrawableButton::backgroundOnColourId, juce::Colours::transparentWhite); getLookAndFeel().setColour(juce::DrawableButton::backgroundOnColourId, juce::Colours::transparentWhite);
if (colour != colourOn) { if (colour != colourOn) {
setClickingTogglesState(true); setClickingTogglesState(true);
} }
setImages(normalImage.get(), nullptr, nullptr, nullptr, normalImageOn.get()); setImages(normalImage.get(), overImage.get(), downImage.get(), disabledImage.get(), normalImageOn.get(), overImageOn.get(), downImageOn.get(), disabledImageOn.get());
if (toggle != nullptr) { if (toggle != nullptr) {
toggle->addListener(this); toggle->addListener(this);
@ -23,7 +37,7 @@ class SvgButton : public juce::DrawableButton, public juce::AudioProcessorParame
} }
} }
SvgButton(juce::String name, juce::String svg, juce::String colour) : SvgButton(name, svg, colour, colour) {} SvgButton(juce::String name, juce::String svg, juce::Colour colour) : SvgButton(name, svg, colour, colour) {}
~SvgButton() override { ~SvgButton() override {
if (toggle != nullptr) { if (toggle != nullptr) {
@ -42,12 +56,20 @@ class SvgButton : public juce::DrawableButton, public juce::AudioProcessorParame
} }
private: private:
std::unique_ptr<juce::Drawable> normalImage; std::unique_ptr<juce::Drawable> normalImage;
std::unique_ptr<juce::Drawable> overImage;
std::unique_ptr<juce::Drawable> downImage;
std::unique_ptr<juce::Drawable> disabledImage;
std::unique_ptr<juce::Drawable> normalImageOn; std::unique_ptr<juce::Drawable> normalImageOn;
std::unique_ptr<juce::Drawable> overImageOn;
std::unique_ptr<juce::Drawable> downImageOn;
std::unique_ptr<juce::Drawable> disabledImageOn;
BooleanParameter* toggle; BooleanParameter* toggle;
void changeSvgColour(juce::XmlElement* xml, juce::String colour) { void changeSvgColour(juce::XmlElement* xml, juce::Colour colour) {
forEachXmlChildElement(*xml, xmlnode) { forEachXmlChildElement(*xml, xmlnode) {
xmlnode->setAttribute("fill", colour); xmlnode->setAttribute("fill", '#' + colour.toDisplayString(false));
} }
} }
}; };

Wyświetl plik

@ -98,6 +98,10 @@ void VolumeComponent::run() {
if (sampleRate != (int) audioProcessor.currentSampleRate) { if (sampleRate != (int) audioProcessor.currentSampleRate) {
resetBuffer(); resetBuffer();
} }
if (buffer.size() == 0) {
continue;
}
consumer = audioProcessor.consumerRegister(buffer); consumer = audioProcessor.consumerRegister(buffer);
audioProcessor.consumerRead(consumer); audioProcessor.consumerRead(consumer);

Wyświetl plik

@ -76,7 +76,7 @@ private:
const double BUFFER_DURATION_SECS = 0.02; const double BUFFER_DURATION_SECS = 0.02;
int sampleRate = DEFAULT_SAMPLE_RATE; int sampleRate = DEFAULT_SAMPLE_RATE;
std::vector<float> buffer; std::vector<float> buffer = std::vector<float>(BUFFER_DURATION_SECS * DEFAULT_SAMPLE_RATE);
std::atomic<float> leftVolume = 0; std::atomic<float> leftVolume = 0;
std::atomic<float> rightVolume = 0; std::atomic<float> rightVolume = 0;

Wyświetl plik

@ -5,7 +5,7 @@
pluginCharacteristicsValue="pluginProducesMidiOut,pluginWantsMidiIn" pluginCharacteristicsValue="pluginProducesMidiOut,pluginWantsMidiIn"
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender" pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
cppLanguageStandard="20" projectLineFeed="&#10;" headerPath="./include" cppLanguageStandard="20" projectLineFeed="&#10;" headerPath="./include"
version="2.0.6" companyName="James H Ball" companyWebsite="https://osci-render.com" version="2.0.7" companyName="James H Ball" companyWebsite="https://osci-render.com"
companyEmail="james@ball.sh"> companyEmail="james@ball.sh">
<MAINGROUP id="j5Ge2T" name="osci-render"> <MAINGROUP id="j5Ge2T" name="osci-render">
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources"> <GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
@ -22,7 +22,9 @@
<FILE id="PxYKbt" name="microphone.svg" compile="0" resource="1" file="Resources/svg/microphone.svg"/> <FILE id="PxYKbt" name="microphone.svg" compile="0" resource="1" file="Resources/svg/microphone.svg"/>
<FILE id="pSc1mq" name="osci.svg" compile="0" resource="1" file="Resources/svg/osci.svg"/> <FILE id="pSc1mq" name="osci.svg" compile="0" resource="1" file="Resources/svg/osci.svg"/>
<FILE id="D2AI1b" name="pencil.svg" compile="0" resource="1" file="Resources/svg/pencil.svg"/> <FILE id="D2AI1b" name="pencil.svg" compile="0" resource="1" file="Resources/svg/pencil.svg"/>
<FILE id="n79IAy" name="record.svg" compile="0" resource="1" file="Resources/svg/record.svg"/>
<FILE id="rXjNlx" name="threshold.svg" compile="0" resource="1" file="Resources/svg/threshold.svg"/> <FILE id="rXjNlx" name="threshold.svg" compile="0" resource="1" file="Resources/svg/threshold.svg"/>
<FILE id="rFYmV8" name="timer.svg" compile="0" resource="1" file="Resources/svg/timer.svg"/>
<FILE id="qC6QiP" name="volume.svg" compile="0" resource="1" file="Resources/svg/volume.svg"/> <FILE id="qC6QiP" name="volume.svg" compile="0" resource="1" file="Resources/svg/volume.svg"/>
</GROUP> </GROUP>
<GROUP id="{F8A3D32C-4187-9A2F-5D78-040259957E9B}" name="text"> <GROUP id="{F8A3D32C-4187-9A2F-5D78-040259957E9B}" name="text">
@ -101,9 +103,12 @@
<FILE id="CdKZCg" name="Matching.h" compile="0" resource="0" file="Source/chinese_postman/Matching.h"/> <FILE id="CdKZCg" name="Matching.h" compile="0" resource="0" file="Source/chinese_postman/Matching.h"/>
</GROUP> </GROUP>
<GROUP id="{CD81913A-7F0E-5898-DA77-5EBEB369DEB1}" name="components"> <GROUP id="{CD81913A-7F0E-5898-DA77-5EBEB369DEB1}" name="components">
<FILE id="M7wyTt" name="AudioRecordingComponent.h" compile="0" resource="0"
file="Source/components/AudioRecordingComponent.h"/>
<FILE id="kUinTt" name="ComponentList.cpp" compile="1" resource="0" <FILE id="kUinTt" name="ComponentList.cpp" compile="1" resource="0"
file="Source/components/ComponentList.cpp"/> file="Source/components/ComponentList.cpp"/>
<FILE id="HGTPEW" name="ComponentList.h" compile="0" resource="0" file="Source/components/ComponentList.h"/> <FILE id="HGTPEW" name="ComponentList.h" compile="0" resource="0" file="Source/components/ComponentList.h"/>
<FILE id="IvySRY" name="DoubleTextBox.h" compile="0" resource="0" file="Source/components/DoubleTextBox.h"/>
<FILE id="poPVxL" name="DraggableListBox.cpp" compile="1" resource="0" <FILE id="poPVxL" name="DraggableListBox.cpp" compile="1" resource="0"
file="Source/components/DraggableListBox.cpp"/> file="Source/components/DraggableListBox.cpp"/>
<FILE id="Y9NEGn" name="DraggableListBox.h" compile="0" resource="0" <FILE id="Y9NEGn" name="DraggableListBox.h" compile="0" resource="0"