diff --git a/Resources/svg/record.svg b/Resources/svg/record.svg
new file mode 100644
index 00000000..a2174671
--- /dev/null
+++ b/Resources/svg/record.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Resources/svg/timer.svg b/Resources/svg/timer.svg
new file mode 100644
index 00000000..35a8847e
--- /dev/null
+++ b/Resources/svg/timer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Source/MainComponent.h b/Source/MainComponent.h
index a9944be1..efb28e4f 100644
--- a/Source/MainComponent.h
+++ b/Source/MainComponent.h
@@ -26,7 +26,7 @@ private:
std::unique_ptr chooser;
juce::TextButton fileButton;
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::TextEditor fileName;
diff --git a/Source/ObjComponent.h b/Source/ObjComponent.h
index 3fedcb2e..3820fc0a 100644
--- a/Source/ObjComponent.h
+++ b/Source/ObjComponent.h
@@ -27,9 +27,9 @@ private:
juce::TextButton resetRotation{"Reset Rotation"};
juce::ToggleButton mouseRotate{"Rotate with Mouse (Esc to disable)"};
- std::shared_ptr fixedRotateX = std::make_shared("fixedRotateX", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateX);
- std::shared_ptr fixedRotateY = std::make_shared("fixedRotateY", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateY);
- std::shared_ptr fixedRotateZ = std::make_shared("fixedRotateZ", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateZ);
+ std::shared_ptr fixedRotateX = std::make_shared("fixedRotateX", juce::String(BinaryData::fixed_rotate_svg), juce::Colours::white, juce::Colours::red, audioProcessor.fixedRotateX);
+ std::shared_ptr fixedRotateY = std::make_shared("fixedRotateY", juce::String(BinaryData::fixed_rotate_svg), juce::Colours::white, juce::Colours::red, audioProcessor.fixedRotateY);
+ std::shared_ptr fixedRotateZ = std::make_shared("fixedRotateZ", juce::String(BinaryData::fixed_rotate_svg), juce::Colours::white, juce::Colours::red, audioProcessor.fixedRotateZ);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ObjComponent)
};
diff --git a/Source/components/AudioRecordingComponent.h b/Source/components/AudioRecordingComponent.h
index 0ae983e6..bc1d3297 100644
--- a/Source/components/AudioRecordingComponent.h
+++ b/Source/components/AudioRecordingComponent.h
@@ -47,6 +47,7 @@
*******************************************************************************/
#pragma once
+#include "DoubleTextBox.h"
//==============================================================================
class AudioRecorder final : public juce::Thread {
@@ -115,6 +116,12 @@ public:
while (!threadShouldExit()) {
consumer = audioProcessor.consumerRegister(buffer);
audioProcessor.consumerRead(consumer);
+
+ if (nextSampleNum >= recordingLength * audioProcessor.currentSampleRate) {
+ stop();
+ stopCallback();
+ continue;
+ }
const juce::ScopedLock sl(writerLock);
int numSamples = buffer.size() / 2;
@@ -135,6 +142,12 @@ public:
}
}
+ void setRecordLength(double recordLength) {
+ recordingLength = recordLength;
+ }
+
+ std::function stopCallback;
+
private:
OscirenderAudioProcessor& audioProcessor;
@@ -145,6 +158,8 @@ private:
std::vector buffer = std::vector(2 << 12);
std::shared_ptr consumer;
+ double recordingLength = 99999999999.0;
+
juce::CriticalSection writerLock;
std::atomic activeWriter { nullptr };
};
@@ -177,10 +192,7 @@ public:
: juce::jmax(30.0, thumbnail.getTotalLength());
auto thumbArea = getLocalBounds();
- thumbnail.drawChannels(g, thumbArea.reduced(2), 0.0, endTime, 2.0f);
- } else {
- g.setFont(14.0f);
- g.drawFittedText("(No file recorded)", getLocalBounds(), juce::Justification::centred, 2);
+ thumbnail.drawChannels(g, thumbArea.reduced(2), 0.0, endTime, 1.0f);
}
}
@@ -204,12 +216,35 @@ 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. When enabled, the recording will automatically stop once the time is reached.");
+
+ recordLength.setValue(1);
recordButton.onClick = [this] {
- if (recorder.isRecording())
- stopRecording();
- else
- startRecording();
+ 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);
@@ -217,9 +252,16 @@ public:
}
void resized() override {
+ double iconSize = 25;
+
auto area = getLocalBounds();
- recordButton.setBounds(area.removeFromLeft(80));
+ 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);
}
@@ -229,16 +271,22 @@ private:
RecordingThumbnail recordingThumbnail;
AudioRecorder recorder{ audioProcessor, recordingThumbnail.getAudioThumbnail() };
- juce::TextButton recordButton{ "Record" };
+ 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.setButtonText("Stop");
recordButton.setColour(juce::TextButton::buttonColourId, juce::Colours::red);
recordButton.setColour(juce::TextButton::textColourOnId, juce::Colours::black);
@@ -247,8 +295,6 @@ private:
void stopRecording() {
recorder.stop();
- recordButton.setButtonText("Record");
-
recordButton.setColour(juce::TextButton::buttonColourId, findColour(juce::TextButton::buttonColourId));
recordButton.setColour(juce::TextButton::textColourOnId, findColour(juce::TextButton::textColourOnId));
diff --git a/Source/components/DoubleTextBox.h b/Source/components/DoubleTextBox.h
new file mode 100644
index 00000000..6fd9a246
--- /dev/null
+++ b/Source/components/DoubleTextBox.h
@@ -0,0 +1,59 @@
+#pragma once
+#include
+
+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;
+};
diff --git a/Source/components/EffectComponent.cpp b/Source/components/EffectComponent.cpp
index c6c2c34d..4a1fe15d 100644
--- a/Source/components/EffectComponent.cpp
+++ b/Source/components/EffectComponent.cpp
@@ -9,7 +9,7 @@ EffectComponent::EffectComponent(OscirenderAudioProcessor& p, Effect& effect, in
sidechainEnabled = effect.parameters[0]->sidechain != nullptr;
if (sidechainEnabled) {
- sidechainButton = std::make_unique(effect.parameters[0]->name, BinaryData::microphone_svg, "white", "red", effect.parameters[0]->sidechain);
+ sidechainButton = std::make_unique(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.");
addAndMakeVisible(*sidechainButton);
}
diff --git a/Source/components/EffectsListComponent.cpp b/Source/components/EffectsListComponent.cpp
index f3611c09..a2c6764d 100644
--- a/Source/components/EffectsListComponent.cpp
+++ b/Source/components/EffectsListComponent.cpp
@@ -97,14 +97,14 @@ std::shared_ptr EffectsListComponent::createComponent(EffectPar
toggle = audioProcessor.perspectiveEffect->fixedRotateZ;
axis = "Z";
}
- std::shared_ptr button = std::make_shared(parameter->name, BinaryData::fixed_rotate_svg, "white", "red", toggle);
+ std::shared_ptr button = std::make_shared(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->onClick = [this, toggle] {
toggle->setBoolValueNotifyingHost(!toggle->getBoolValue());
};
return button;
} else if (parameter->paramID == "perspectiveStrength") {
- std::shared_ptr button = std::make_shared(parameter->name, BinaryData::pencil_svg, "white", "red");
+ std::shared_ptr button = std::make_shared(parameter->name, BinaryData::pencil_svg, juce::Colours::white, juce::Colours::red);
std::weak_ptr weakButton = button;
button->setEdgeIndent(5);
button->setToggleState(editor.editingPerspective, juce::dontSendNotification);
diff --git a/Source/components/SvgButton.h b/Source/components/SvgButton.h
index 9a654c15..4dbece9f 100644
--- a/Source/components/SvgButton.h
+++ b/Source/components/SvgButton.h
@@ -3,19 +3,33 @@
class SvgButton : public juce::DrawableButton, public juce::AudioProcessorParameter::Listener, public juce::AsyncUpdater {
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);
+
changeSvgColour(doc.get(), colour);
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);
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);
if (colour != colourOn) {
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) {
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 {
if (toggle != nullptr) {
@@ -42,12 +56,20 @@ class SvgButton : public juce::DrawableButton, public juce::AudioProcessorParame
}
private:
std::unique_ptr normalImage;
+ std::unique_ptr overImage;
+ std::unique_ptr downImage;
+ std::unique_ptr disabledImage;
+
std::unique_ptr normalImageOn;
+ std::unique_ptr overImageOn;
+ std::unique_ptr downImageOn;
+ std::unique_ptr disabledImageOn;
+
BooleanParameter* toggle;
- void changeSvgColour(juce::XmlElement* xml, juce::String colour) {
+ void changeSvgColour(juce::XmlElement* xml, juce::Colour colour) {
forEachXmlChildElement(*xml, xmlnode) {
- xmlnode->setAttribute("fill", colour);
+ xmlnode->setAttribute("fill", '#' + colour.toDisplayString(false));
}
}
};
diff --git a/osci-render.jucer b/osci-render.jucer
index dbe2d7db..4942425c 100644
--- a/osci-render.jucer
+++ b/osci-render.jucer
@@ -22,7 +22,9 @@
+
+
@@ -106,6 +108,7 @@
+