From af5efd8b098962524f647600290b3407a50a16f2 Mon Sep 17 00:00:00 2001 From: essej Date: Tue, 14 Jun 2022 19:11:57 -0400 Subject: [PATCH] added output recording feature. moved settings to a separate popup panel instead of a menu --- CMakeLists.txt | 18 +- Source/CustomLookAndFeel.cpp | 6 +- Source/CustomStandaloneFilterApp.cpp | 4 +- Source/CustomStandaloneFilterWindow.h | 4 +- Source/GenericItemChooser.cpp | 354 +++++++++++ Source/GenericItemChooser.h | 110 ++++ Source/OptionsView.cpp | 843 ++++++++++++++++++++++++++ Source/OptionsView.h | 148 +++++ Source/PS_Source/globals.h | 4 +- Source/PluginEditor.cpp | 440 +++++++------- Source/PluginEditor.h | 22 +- Source/PluginProcessor.cpp | 216 ++++++- Source/PluginProcessor.h | 47 +- Source/SonoChoiceButton.cpp | 245 ++++++++ Source/SonoChoiceButton.h | 90 +++ Source/SonoTextButton.cpp | 409 +++++++++++++ Source/SonoTextButton.h | 74 +++ images/record_input.svg | 132 ++++ images/record_input_active.svg | 132 ++++ images/record_output.svg | 137 +++++ images/record_output_active.svg | 137 +++++ 21 files changed, 3309 insertions(+), 263 deletions(-) create mode 100644 Source/GenericItemChooser.cpp create mode 100644 Source/GenericItemChooser.h create mode 100644 Source/OptionsView.cpp create mode 100644 Source/OptionsView.h create mode 100644 Source/SonoChoiceButton.cpp create mode 100644 Source/SonoChoiceButton.h create mode 100644 Source/SonoTextButton.cpp create mode 100644 Source/SonoTextButton.h create mode 100644 images/record_input.svg create mode 100644 images/record_input_active.svg create mode 100644 images/record_output.svg create mode 100644 images/record_output_active.svg diff --git a/CMakeLists.txt b/CMakeLists.txt index d4f6b02..6938012 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,9 +36,9 @@ endif() # `project()` command. `project()` sets up some helpful variables that describe source/binary # directories, and the current project version. This is a standard CMake command. -project(PaulXStretch VERSION 1.5.4) +project(PaulXStretch VERSION 1.6.0) -set(BUILDVERSION 111) +set(BUILDVERSION 112) # If you've installed JUCE somehow (via a package manager, or directly using the CMake install @@ -277,12 +277,20 @@ function(sono_add_custom_plugin_target target_name product_name formats is_instr Source/CustomLookAndFeel.h Source/CustomStandaloneFilterApp.cpp Source/CustomStandaloneFilterWindow.h + Source/GenericItemChooser.cpp + Source/GenericItemChooser.h + Source/SonoChoiceButton.cpp + Source/SonoChoiceButton.h + Source/SonoTextButton.cpp + Source/SonoTextButton.h Source/PluginEditor.cpp Source/PluginEditor.h Source/PluginProcessor.cpp Source/PluginProcessor.h Source/RenderSettingsComponent.cpp Source/RenderSettingsComponent.h + Source/OptionsView.cpp + Source/OptionsView.h Source/envelope_component.cpp Source/envelope_component.h Source/jcdp_envelope.h @@ -401,8 +409,10 @@ function(sono_add_custom_plugin_target target_name product_name formats is_instr images/play_icon.svg images/power.svg images/power_sel.svg - images/record.svg - images/record_active.svg + images/record_input.svg + images/record_input_active.svg + images/record_output.svg + images/record_output_active.svg images/skipback_icon.svg ) diff --git a/Source/CustomLookAndFeel.cpp b/Source/CustomLookAndFeel.cpp index ea5c915..3cfcb79 100644 --- a/Source/CustomLookAndFeel.cpp +++ b/Source/CustomLookAndFeel.cpp @@ -243,10 +243,12 @@ void CustomLookAndFeel::createTabTextLayout (const TabBarButton& button, float l Colour colour, TextLayout& textLayout) { float hscale = 0.6f; + float xhscale = 0.6f; #if JUCE_IOS hscale = 0.5f; + xhscale = 0.5f; #endif - float fontsize = button.getExtraComponent() != nullptr ? jmin(depth, 32.0f) * hscale : jmin(depth, 32.0f) * hscale; + float fontsize = button.getExtraComponent() != nullptr ? jmin(depth, 32.0f) * xhscale : jmin(depth, 32.0f) * hscale; Font font = myFont.withHeight(fontsize * fontScale); font.setUnderline (button.hasKeyboardFocus (false)); @@ -1174,7 +1176,7 @@ void CustomLookAndFeel::drawDrawableButton (Graphics& g, DrawableButton& button, int textH = 0; int textW = 0; - float imageratio = 0.75f; + float imageratio = 0.85f; //if (SonoDrawableButton* const sonobutt = dynamic_cast (&button)) { // imageratio = sonobutt->getForegroundImageRatio(); diff --git a/Source/CustomStandaloneFilterApp.cpp b/Source/CustomStandaloneFilterApp.cpp index 101ebb0..2b388d7 100644 --- a/Source/CustomStandaloneFilterApp.cpp +++ b/Source/CustomStandaloneFilterApp.cpp @@ -131,8 +131,8 @@ public: mainWindow->pluginHolder->saveAudioDeviceState(); if (auto * sonoproc = dynamic_cast(mainWindow->pluginHolder->processor.get())) { - if (sonoproc->getBoolParameter(cpi_pause_enabled)->get() && !sonoproc->isRecordingEnabled() - && !mainWindow->pluginHolder->isInterAppAudioConnected()) { + if (sonoproc->getBoolParameter(cpi_pause_enabled)->get() && !sonoproc->isInputRecordingEnabled() + && !sonoproc->isRecordingToFile() && !mainWindow->pluginHolder->isInterAppAudioConnected()) { // shutdown audio engine DBG("not active, shutting down audio"); mainWindow->getDeviceManager().closeAudioDevice(); diff --git a/Source/CustomStandaloneFilterWindow.h b/Source/CustomStandaloneFilterWindow.h index b1ba673..42f9138 100644 --- a/Source/CustomStandaloneFilterWindow.h +++ b/Source/CustomStandaloneFilterWindow.h @@ -327,7 +327,7 @@ public: settings->setValue ("audioSetup", xml.get()); #if ! (JUCE_IOS || JUCE_ANDROID) - settings->setValue ("shouldMuteInput", (bool) shouldMuteInput.getValue()); + // settings->setValue ("shouldMuteInput", (bool) shouldMuteInput.getValue()); #endif } } @@ -343,7 +343,7 @@ public: savedState = settings->getXmlValue ("audioSetup"); #if ! (JUCE_IOS || JUCE_ANDROID) - shouldMuteInput.setValue (settings->getBoolValue ("shouldMuteInput", true)); + // shouldMuteInput.setValue (settings->getBoolValue ("shouldMuteInput", true)); #endif } diff --git a/Source/GenericItemChooser.cpp b/Source/GenericItemChooser.cpp new file mode 100644 index 0000000..7d454d2 --- /dev/null +++ b/Source/GenericItemChooser.cpp @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPLv3-or-later WITH Appstore-exception +// Copyright (C) 2020 Jesse Chappell + + +#include "GenericItemChooser.h" + +enum { + nameTextColourId = 0x1002830, + currentNameTextColourId = 0x1002850, + selectedColourId = 0x1002840, + separatorColourId = 0x1002860, + disabledColourId = 0x1002870 +}; + + +CallOutBox& GenericItemChooser::launchPopupChooser(const Array & items, Rectangle targetBounds, Component * targetComponent, GenericItemChooser::Listener * listener, int tag, int selectedIndex, int maxheight, bool dismissSel) +{ + + auto chooser = std::make_unique(items, tag); + chooser->dismissOnSelected = dismissSel; + if (selectedIndex >= 0) { + chooser->setCurrentRow(selectedIndex); + } + + if (listener) { + chooser->addListener(listener); + } + if (maxheight > 0) { + chooser->setMaxHeight(maxheight); + } + + + CallOutBox & box = CallOutBox::launchAsynchronously (std::move(chooser), targetBounds, targetComponent); + box.setDismissalMouseClicksAreAlwaysConsumed(true); + // box.setArrowSize(0); + box.grabKeyboardFocus(); + + return box; +} + +CallOutBox& GenericItemChooser::launchPopupChooser(const Array & items, juce::Rectangle targetBounds, Component * targetComponent, std::function onSelectedFunction, int selectedIndex, int maxheight, bool dismissSel) +{ + auto chooser = std::make_unique(items, 0); + chooser->dismissOnSelected = dismissSel; + + if (selectedIndex >= 0) { + chooser->setCurrentRow(selectedIndex); + } + + chooser->onSelected = onSelectedFunction; + + if (maxheight > 0) { + chooser->setMaxHeight(maxheight); + } + + CallOutBox & box = CallOutBox::launchAsynchronously (std::move(chooser), targetBounds, targetComponent); + box.setDismissalMouseClicksAreAlwaysConsumed(true); + // box.setArrowSize(0); + box.grabKeyboardFocus(); + + return box; +} + + +GenericItemChooser::GenericItemChooser(const Array & items_, int tag_) : font (16.0, Font::plain), catFont(15.0, Font::plain), items(items_), tag(tag_) + +{ + currentIndex = -1; +#if JUCE_IOS || JUCE_ANDROID + rowHeight = 42; +#else + rowHeight = 32; +#endif + numRows = items.size(); + + // Create our table component and add it to this component.. + addAndMakeVisible (table); + table.setModel (this); + + // give it a border + + table.setColour (ListBox::outlineColourId, Colour::fromFloatRGBA(0.0, 0.0, 0.0, 0.0)); + table.setColour (ListBox::backgroundColourId, Colour::fromFloatRGBA(0.1, 0.1, 0.1, 1.0f)); + table.setColour (ListBox::textColourId, Colours::whitesmoke.withAlpha(0.8f)); + + setColour (nameTextColourId, Colour::fromFloatRGBA(1.0f, 1.0f, 1.0f, 0.8f)); + setColour (currentNameTextColourId, Colour::fromFloatRGBA(0.4f, 0.8f, 1.0f, 0.9f)); + setColour (selectedColourId, Colour (0xff3d70c8).withAlpha(0.5f)); + setColour (separatorColourId, Colour::fromFloatRGBA(1.0f, 1.0f, 1.0f, 0.5f)); + setColour (disabledColourId, Colour::fromFloatRGBA(0.7f, 0.7f, 0.7f, 0.7f)); + + + table.setOutlineThickness (0); + + + + //table.getViewport()->setScrollOnDragEnabled(true); + table.getViewport()->setScrollBarsShown(true, false); + table.getViewport()->setScrollOnDragEnabled(true); + table.setRowSelectedOnMouseDown(true); + table.setRowClickedOnMouseDown(false); + table.setMultipleSelectionEnabled (false); + table.setRowHeight(rowHeight); + + int newh = (rowHeight) * numRows + 4; + setSize(getAutoWidth(), newh); + +} + +GenericItemChooser::~GenericItemChooser() +{ +} + +void GenericItemChooser::setCurrentRow(int index) +{ + currentIndex = index; + SparseSet selrows; + selrows.addRange(Range(index,index+1)); + table.setSelectedRows(selrows); + table.updateContent(); +} + + +void GenericItemChooser::setItems(const Array & items_) +{ + items = items_; + numRows = items.size(); + table.updateContent(); + + int newh = (rowHeight) * numRows; + + setSize(getAutoWidth(), newh); + +} + +int GenericItemChooser::getAutoWidth() +{ + int targw = 60; + + for (int i=0; i < items.size(); ++i) { + int tsize = font.getStringWidth(items[i].name); + if (items[i].image.isValid()) { + tsize += rowHeight - 8; + } + targw = jmax(targw, tsize); + } + + return targw + 30; +} + + +void GenericItemChooser::setRowHeight(int ht) +{ + rowHeight = ht; + table.setRowHeight(rowHeight); + int newh = (rowHeight+2) * numRows; + if (maxHeight > 0) { + newh = jmin(newh, maxHeight); + } + setSize(getAutoWidth(), newh); +} + +void GenericItemChooser::setMaxHeight(int ht) +{ + maxHeight = ht; + + int newh = (rowHeight+2) * numRows; + if (maxHeight > 0) { + newh = jmin(newh, maxHeight); + } + setSize(getAutoWidth(), newh); +} + + + // This is overloaded from TableListBoxModel, and must return the total number of rows in our table +int GenericItemChooser::getNumRows() +{ + return numRows; +} + +String GenericItemChooser::getNameForRow (int rowNumber) +{ + if (rowNumber< items.size()) { + return items[rowNumber].name; + } + return ListBoxModel::getNameForRow(rowNumber); +} + + +void GenericItemChooser::listBoxItemClicked (int rowNumber, const MouseEvent& e) +{ + + DBG("listbox clicked"); + + if (items[rowNumber].disabled) { + // not selectable + return; + } + + listeners.call (&GenericItemChooser::Listener::genericItemChooserSelected, this, rowNumber); + + if (onSelected) { + onSelected(this, rowNumber); + } + + if (dismissOnSelected) { + if (CallOutBox* const cb = findParentComponentOfClass()) { + cb->dismiss(); + } + } else { + setCurrentRow(rowNumber); + repaint(); + } +} + +void GenericItemChooser::selectedRowsChanged(int lastRowSelected) +{ + // notify listeners + DBG("Selected rows changed"); +} + +void GenericItemChooser::deleteKeyPressed (int) +{ + DBG("delete key pressed"); + +} + +void GenericItemChooser::returnKeyPressed (int rowNumber) +{ + DBG("return key pressed: " << rowNumber); + + listeners.call (&GenericItemChooser::Listener::genericItemChooserSelected, this, rowNumber); + + if (rowNumber < items.size() && items[rowNumber].disabled) { + // not selectable + return; + } + + if (onSelected) { + onSelected(this, rowNumber); + } + + if (dismissOnSelected) { + + if (CallOutBox* const cb = findParentComponentOfClass()) { + cb->giveAwayKeyboardFocus(); + cb->dismiss(); + } + } else { + setCurrentRow(rowNumber); + repaint(); + } +} + + +void GenericItemChooser::paintListBoxItem (int rowNumber, Graphics &g, int width, int height, bool rowIsSelected) +{ + + if (items[rowNumber].separator) { + g.setColour (findColour(separatorColourId)); + g.drawLine(0, 0, width, 0); + } + + if (rowIsSelected && !items[rowNumber].disabled) { + g.setColour (findColour(selectedColourId)); + g.fillRect(Rectangle(0,0,width,height)); + } + + if (items[rowNumber].disabled) { + g.setColour (findColour(disabledColourId)); + } + else if (rowNumber == currentIndex) { + g.setColour (findColour(currentNameTextColourId)); + } + else { + g.setColour (findColour(nameTextColourId)); + } + + g.setFont (font); + + int imagewidth = 0; + + + + if (rowNumber < items.size()) { + if (items[rowNumber].image.isValid()) { + imagewidth = height-8; + //g.drawImage(items[rowNumber].image, Rectangle(2, 2, imagewidth, height - 4)); + g.drawImageWithin(items[rowNumber].image, 2, 4, imagewidth, imagewidth, RectanglePlacement(RectanglePlacement::centred|RectanglePlacement::onlyReduceInSize)); + + + } + } + + + /* + + int imagewidth = height * 0.75; + + if (rowNumber == 0) { + g.drawImageWithin(wheelImage, 2, 2, imagewidth, height - 4, RectanglePlacement::centred|RectanglePlacement::onlyReduceInSize); + } + else if (rowNumber == 1) { + g.drawImageWithin(stringImage, 2, 2, imagewidth, height - 4, RectanglePlacement::centred|RectanglePlacement::onlyReduceInSize); + } + else { + g.drawImageWithin(keyboardImage, 2, 2, imagewidth, height - 4, RectanglePlacement::centred|RectanglePlacement::onlyReduceInSize); + } + */ + + String text = items[rowNumber].name; + //DBG("Paint " << text); + + + //g.drawFittedText (text, imagewidth + 10, 0, width - (imagewidth+8), height, Justification::centredLeft, 1, 0.5); + g.drawFittedText (text, imagewidth + 8, 0, width - (imagewidth+8), height, Justification::centredLeft, 1, 0.5); +} + + + +void GenericItemChooser::buttonClicked (Button* buttonThatWasClicked) +{ + //AppState * app = AppState::getInstance(); + + +} + + +void GenericItemChooser::paint (Graphics& g) +{ + //g.fillAll (Colour (0xff000000)); +} + + +//============================================================================== +void GenericItemChooser::resized() +{ + // position our table with a gap around its edge + int keywidth = 50; + int bottomButtHeight = 40; + + table.setBoundsInset (BorderSize(2,2,2,2)); + +// table.setTouchScrollScale(1.0f / AppState::getInstance()->scaleFactor); + + //int bottbuttwidth = (int) (0.3333f * (getWidth()-keywidth)); + //int addbuttwidth = (getWidth()-keywidth) - 2*bottbuttwidth - 8; + + //allButton->setBounds(keywidth+2, getHeight()-bottomButtHeight-4, bottbuttwidth, bottomButtHeight); + //favsButton->setBounds(keywidth+2+bottbuttwidth, getHeight()-bottomButtHeight-4, bottbuttwidth, bottomButtHeight); + //addFavButton->setBounds(getWidth()-addbuttwidth-4, getHeight()-bottomButtHeight-4, addbuttwidth, bottomButtHeight); + +} + diff --git a/Source/GenericItemChooser.h b/Source/GenericItemChooser.h new file mode 100644 index 0000000..ba2722c --- /dev/null +++ b/Source/GenericItemChooser.h @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPLv3-or-later WITH Appstore-exception +// Copyright (C) 2020 Jesse Chappell + + +#pragma once + +#include "JuceHeader.h" + + +struct GenericItemChooserItem +{ + struct UserData { + virtual ~UserData() {} + }; + + GenericItemChooserItem() : image(Image()) {} + GenericItemChooserItem(const String & name_, const Image & image_=Image(), std::shared_ptr udata = nullptr, bool withSeparator=false, bool disabled_=false) : name(name_), image(image_), userdata(udata), separator(withSeparator), disabled(disabled_) {} + String name; + Image image; + std::shared_ptr userdata; + bool separator = false; + bool disabled = false; +}; + +class GenericItemChooser : public Component, public ListBoxModel, public Button::Listener +{ +public: + GenericItemChooser(const Array & items_, int tag=0); + virtual ~GenericItemChooser(); + + class Listener { + public: + virtual ~Listener() {} + virtual void genericItemChooserSelected(GenericItemChooser *comp, int index) {} + }; + + void setItems(const Array & items); + const Array & getItems() const { return items; } + + // This is overloaded from TableListBoxModel, and must return the total number of rows in our table + int getNumRows() override; + String getNameForRow (int rowNumber) override; + void deleteKeyPressed (int) override; + void returnKeyPressed (int) override; + + void paintListBoxItem (int rowNumber, Graphics &g, int width, int height, bool rowIsSelected) override; + + + // This is overloaded from TableListBoxModel, and must update any custom components that we're using + //Component* refreshComponentForCell (int rowNumber, int columnId, bool /*isRowSelected*/, + // Component* existingComponentToUpdate) override; + + + void setCurrentRow(int index); + int getCurrentRow() const { return currentIndex; } + + //============================================================================== + void resized() override; + + void listBoxItemClicked (int rowNumber, const MouseEvent& e) override; + void selectedRowsChanged(int lastRowSelected) override; + + void buttonClicked (Button* buttonThatWasClicked) override; + void paint (Graphics& g) override; + + void addListener(Listener * listener) { listeners.add(listener); } + void removeListener(Listener * listener) { listeners.remove(listener); } + + void setRowHeight(int ht); + int getRowHeight() const { return rowHeight; } + + void setMaxHeight(int ht); + int getMaxHeight() const { return maxHeight; } + + void setTag(int tag_) { tag = tag_;} + int getTag() const { return tag; } + + static CallOutBox& launchPopupChooser(const Array & items, juce::Rectangle targetBounds, Component * targetComponent, GenericItemChooser::Listener * listener, int tag = 0, int selectedIndex=-1, int maxheight=0, bool dismissSel=true); + + static CallOutBox& launchPopupChooser(const Array & items, juce::Rectangle targetBounds, Component * targetComponent, std::function onSelectedFunction, int selectedIndex=-1, int maxheight=0, bool dismissSel=true); + + + std::function onSelected; + + bool dismissOnSelected = true; + +private: + + int getAutoWidth(); + + ListenerList listeners; + + ListBox table; // the table component itself + Font font; + Font catFont; + + int numRows; // The number of rows of data we've got + bool sortDirection; + int selectedRow; + int rowHeight; + int maxHeight = 0; + + //StringArray items; + Array items; + + int currentIndex; + int tag; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericItemChooser) +}; diff --git a/Source/OptionsView.cpp b/Source/OptionsView.cpp new file mode 100644 index 0000000..6ca0a13 --- /dev/null +++ b/Source/OptionsView.cpp @@ -0,0 +1,843 @@ +// SPDX-License-Identifier: GPLv3-or-later WITH Appstore-exception +// Copyright (C) 2021 Jesse Chappell + +#include "OptionsView.h" + + + +class PaulxstretchOptionsTabbedComponent : public TabbedComponent +{ +public: + PaulxstretchOptionsTabbedComponent(TabbedButtonBar::Orientation orientation, OptionsView & editor_) : TabbedComponent(orientation), editor(editor_) { + + } + + void currentTabChanged (int newCurrentTabIndex, const String& newCurrentTabName) override { + + editor.optionsTabChanged(newCurrentTabIndex); + } + +protected: + OptionsView & editor; + +}; + + +enum { + nameTextColourId = 0x1002830, + selectedColourId = 0x1002840, + separatorColourId = 0x1002850, +}; + + + + +OptionsView::OptionsView(PaulstretchpluginAudioProcessor& proc, std::function getaudiodevicemanager) +: Component(), getAudioDeviceManager(getaudiodevicemanager), processor(proc), smallLNF(14), sonoSliderLNF(13) +{ + setColour (nameTextColourId, Colour::fromFloatRGBA(1.0f, 1.0f, 1.0f, 0.9f)); + setColour (selectedColourId, Colour::fromFloatRGBA(0.0f, 0.4f, 0.8f, 0.5f)); + setColour (separatorColourId, Colour::fromFloatRGBA(0.3f, 0.3f, 0.3f, 0.3f)); + + sonoSliderLNF.textJustification = Justification::centredRight; + sonoSliderLNF.sliderTextJustification = Justification::centredRight; + + mOptionsComponent = std::make_unique(); + + mSettingsTab = std::make_unique(TabbedButtonBar::Orientation::TabsAtTop); + mSettingsTab->setTabBarDepth(36); + mSettingsTab->setOutline(0); + mSettingsTab->getTabbedButtonBar().setMinimumTabScaleFactor(0.1f); + //mSettingsTab->addComponentListener(this); + + + mOptionsLoadFileWithPluginButton = std::make_unique(TRANS("Load file with plugin state")); + mOptionsLoadFileWithPluginButton->addListener(this); + mOptionsLoadFileWithPluginButton->onClick = [this] () { + toggleBool(processor.m_load_file_with_state); + }; + + mOptionsPlayWithTransportButton = std::make_unique(TRANS("Play when host transport running")); + mOptionsPlayWithTransportButton->onClick = [this] () { + toggleBool(processor.m_play_when_host_plays); + }; + + mOptionsCaptureWithTransportButton = std::make_unique(TRANS("Capture when host transport running")); + mOptionsCaptureWithTransportButton->onClick = [this] () { + toggleBool(processor.m_capture_when_host_plays); + }; + + mOptionsRestorePlayStateButton = std::make_unique(TRANS("Restore playing state")); + mOptionsRestorePlayStateButton->onClick = [this] () { + toggleBool(processor.m_restore_playstate); + }; + + mOptionsMutePassthroughWhenCaptureButton = std::make_unique(TRANS("Mute passthrough while capturing")); + mOptionsMutePassthroughWhenCaptureButton->onClick = [this] () { + toggleBool(processor.m_mute_while_capturing); + }; + + mOptionsMuteProcessedWhenCaptureButton = std::make_unique(TRANS("Mute processed audio output while capturing")); + mOptionsMuteProcessedWhenCaptureButton->onClick = [this] () { + toggleBool(processor.m_mute_processed_while_capturing); + }; + + mOptionsSaveCaptureToDiskButton = std::make_unique(TRANS("Save captured audio to disk")); + mOptionsSaveCaptureToDiskButton->onClick = [this] () { + toggleBool(processor.m_save_captured_audio); + }; + + mOptionsEndRecordingAfterMaxButton = std::make_unique(TRANS("End recording after capturing max length")); + mOptionsEndRecordingAfterMaxButton->onClick = [this] () { + toggleBool(processor.m_auto_finish_record); + }; + + mOptionsSliderSnapToMouseButton = std::make_unique(TRANS("Sliders jump to position")); + mOptionsSliderSnapToMouseButton->onClick = [this] () { + toggleBool(processor.m_use_jumpsliders); + if (updateSliderSnap) + updateSliderSnap(); + }; + + + mOptionsShowTechnicalInfoButton = std::make_unique(TRANS("Show technical info in waveform")); + mOptionsShowTechnicalInfoButton->onClick = [this] () { + toggleBool(processor.m_show_technical_info); + }; + + + + mRecFormatChoice = std::make_unique(); + mRecFormatChoice->addChoiceListener(this); + mRecFormatChoice->addItem(TRANS("FLAC"), PaulstretchpluginAudioProcessor::FileFormatFLAC); + mRecFormatChoice->addItem(TRANS("WAV"), PaulstretchpluginAudioProcessor::FileFormatWAV); + mRecFormatChoice->addItem(TRANS("OGG"), PaulstretchpluginAudioProcessor::FileFormatOGG); + + mRecBitsChoice = std::make_unique(); + mRecBitsChoice->addChoiceListener(this); + mRecBitsChoice->addItem(TRANS("16 bit"), 16); + mRecBitsChoice->addItem(TRANS("24 bit"), 24); + mRecBitsChoice->addItem(TRANS("32 bit"), 32); + + + mRecFormatStaticLabel = std::make_unique