Skip to content

Commit

Permalink
Browser auto-preview (#1451)
Browse files Browse the repository at this point in the history
This activates preview-without-zone DSP from the browser UI
if the toggle is on and you long hold a single file sample.
  • Loading branch information
baconpaul authored Nov 20, 2024
1 parent ba17b04 commit 10a872a
Show file tree
Hide file tree
Showing 10 changed files with 344 additions and 21 deletions.
113 changes: 100 additions & 13 deletions src-ui/app/browser-ui/BrowserPane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "sst/jucegui/components/Label.h"
#include "sst/jucegui/components/NamedPanelDivider.h"
#include "sst/jucegui/components/TextPushButton.h"
#include "sst/jucegui/components/GlyphButton.h"
#include "sst/plugininfra/strnatcmp.h"

namespace scxt::ui::app::browser_ui
Expand Down Expand Up @@ -290,9 +291,35 @@ struct DriveFSListBoxRow : public juce::Component
DriveFSListBoxModel *model{nullptr};

juce::ListBox *enclosingBox() { return browserPane->devicesPane->driveFSArea->lbox.get(); }
bool isDragging;
bool isDragging{false};
bool isMouseDownWithoutDrag{false};
bool hasStartedPreview{false};

void mouseDown(const juce::MouseEvent &event) override
{
isMouseDownWithoutDrag = true;
if (browserPane->autoPreviewEnabled && isFile())
{
juce::Timer::callAfterDelay(500, [w = juce::Component::SafePointer(this)]() {
if (!w)
return;
if (!w->isMouseDownWithoutDrag)
return;

const auto &data = w->browserPane->devicesPane->driveFSArea->contents;
const auto &entry = data[w->rowNumber];
if (browser::Browser::isLoadableSingleSample(entry.path()))
{
w->hasStartedPreview = true;
namespace cmsg = scxt::messaging::client;
scxt::messaging::client::clientSendToSerialization(
cmsg::PreviewBrowserSample({true, data[w->rowNumber].path().u8string()}),
w->browserPane->editor->msgCont);
w->repaint();
}
});
}

enclosingBox()->selectRowsBasedOnModifierKeys(rowNumber, event.mods, false);

if (event.mods.isPopupMenu())
Expand All @@ -309,6 +336,9 @@ struct DriveFSListBoxRow : public juce::Component
if (!isFile())
return;

isMouseDownWithoutDrag = false;

stopPreview();
if (!isDragging && e.getDistanceFromDragStart() > 1.5f)
{
if (auto *container = juce::DragAndDropContainer::findParentDragContainerFor(this))
Expand All @@ -325,6 +355,8 @@ struct DriveFSListBoxRow : public juce::Component

void mouseUp(const juce::MouseEvent &event) override
{
isMouseDownWithoutDrag = false;
stopPreview();
if (isDragging)
{
isDragging = false;
Expand All @@ -334,8 +366,23 @@ struct DriveFSListBoxRow : public juce::Component
enclosingBox()->selectRowsBasedOnModifierKeys(rowNumber, event.mods, true);
}
}

void stopPreview()
{
if (hasStartedPreview)
{
hasStartedPreview = false;
repaint();
namespace cmsg = scxt::messaging::client;

scxt::messaging::client::clientSendToSerialization(
cmsg::PreviewBrowserSample({false, ""}), browserPane->editor->msgCont);
}
}

void mouseDoubleClick(const juce::MouseEvent &event) override
{
isMouseDownWithoutDrag = false;
const auto &data = browserPane->devicesPane->driveFSArea->contents;
if (rowNumber >= 0 && rowNumber < data.size())
{
Expand Down Expand Up @@ -422,6 +469,13 @@ struct DriveFSListBoxRow : public juce::Component
jcmp::GlyphPainter::paintGlyph(g, r, jcmp::GlyphPainter::TUNING,
textColor.withAlpha(0.5f));
}

if (hasStartedPreview)
{
auto q = r.translated(getWidth() - r.getHeight() - 2, 0);
jcmp::GlyphPainter::paintGlyph(g, q, jcmp::GlyphPainter::SPEAKER,
textColor.withAlpha(0.5f));
}
g.setColour(textColor);
g.drawText(entry.path().filename().u8string(), 14, 1, width - 16, height - 2,
juce::Justification::centredLeft);
Expand Down Expand Up @@ -476,6 +530,32 @@ struct SearchPane : TempPane
SearchPane(SCXTEditor *e) : TempPane(e, "Search") {}
};

struct BrowserPaneFooter : HasEditor, juce::Component
{
BrowserPane *parent{nullptr};
std::unique_ptr<jcmp::ToggleButton> autoPreview;
std::unique_ptr<jcmp::GlyphButton> preview;
std::unique_ptr<jcmp::HSlider> previewLevel;
std::unique_ptr<connectors::DirectBooleanPayloadDataAttachment> autoPreviewAtt;

BrowserPaneFooter(SCXTEditor *e, BrowserPane *p) : HasEditor(e), parent(p)
{
autoPreview = std::make_unique<jcmp::ToggleButton>();
autoPreview->setDrawMode(sst::jucegui::components::ToggleButton::DrawMode::GLYPH_WITH_BG);
autoPreview->setGlyph(sst::jucegui::components::GlyphPainter::SPEAKER);
autoPreviewAtt = std::make_unique<connectors::DirectBooleanPayloadDataAttachment>(
[](auto) {}, parent->autoPreviewEnabled);
autoPreview->setSource(autoPreviewAtt.get());
addAndMakeVisible(*autoPreview);
}
void resized() override
{
auto r = getLocalBounds();
autoPreview->setBounds(r.withWidth(r.getHeight()));
r = r.translated(r.getHeight() + 2, 0);
}
};

struct sfData : sst::jucegui::data::Discrete
{
BrowserPane *browserPane{nullptr};
Expand All @@ -500,10 +580,10 @@ struct sfData : sst::jucegui::data::Discrete
switch (i)
{
case 0:
return "DEVICES";
return "LOCATIONS";
// case 1:
// return "FAVORITES";
case 1:
return "FAVORITES";
case 2:
return "SEARCH";
}
}
Expand All @@ -512,7 +592,7 @@ struct sfData : sst::jucegui::data::Discrete
return "-error-";
}

int getMax() const override { return 2; }
int getMax() const override { return 1; }
};

BrowserPane::BrowserPane(SCXTEditor *e)
Expand All @@ -528,11 +608,14 @@ BrowserPane::BrowserPane(SCXTEditor *e)

devicesPane = std::make_unique<DevicesPane>(this);
addChildComponent(*devicesPane);
favoritesPane = std::make_unique<FavoritesPane>(e);
addChildComponent(*favoritesPane);
// favoritesPane = std::make_unique<FavoritesPane>(e);
// addChildComponent(*favoritesPane);
searchPane = std::make_unique<SearchPane>(e);
addChildComponent(*searchPane);

footerArea = std::make_unique<BrowserPaneFooter>(e, this);
addAndMakeVisible(*footerArea);

selectPane(selectedPane);

resetRoots();
Expand All @@ -543,13 +626,15 @@ BrowserPane::~BrowserPane() { selectedFunction->setSource(nullptr); }
void BrowserPane::resized()
{
auto r = getContentArea();
auto sel = r.withHeight(22);
auto ct = r.withTrimmedTop(25);
auto sel = r.withHeight(18);
auto ct = r.withTrimmedTop(21).withTrimmedBottom(23);
auto ft = r.withTop(r.getBottom() - 21).withTrimmedBottom(2);
selectedFunction->setBounds(sel);

devicesPane->setBounds(ct);
favoritesPane->setBounds(ct);
// favoritesPane->setBounds(ct);
searchPane->setBounds(ct);
footerArea->setBounds(ft);
}

void BrowserPane::resetRoots()
Expand All @@ -563,10 +648,12 @@ void BrowserPane::resetRoots()
void BrowserPane::selectPane(int i)
{
selectedPane = i;
if (selectedPane < 0 || selectedPane > 1)
selectedPane = 0;

devicesPane->setVisible(i == 0);
favoritesPane->setVisible(i == 1);
searchPane->setVisible(i == 2);
devicesPane->setVisible(selectedPane == 0);
// favoritesPane->setVisible(i == 1);
searchPane->setVisible(selectedPane == 1);
repaint();
}
} // namespace scxt::ui::app::browser_ui
4 changes: 4 additions & 0 deletions src-ui/app/browser-ui/BrowserPane.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace scxt::ui::app::browser_ui // a bit clumsy to distinguis from scxt::bro
struct DevicesPane;
struct FavoritesPane;
struct SearchPane;
struct BrowserPaneFooter;
struct BrowserPane : public app::HasEditor, sst::jucegui::components::NamedPanel
{
std::unique_ptr<sst::jucegui::components::ToggleButtonRadioGroup> selectedFunction;
Expand All @@ -57,9 +58,12 @@ struct BrowserPane : public app::HasEditor, sst::jucegui::components::NamedPanel
std::unique_ptr<DevicesPane> devicesPane;
std::unique_ptr<FavoritesPane> favoritesPane;
std::unique_ptr<SearchPane> searchPane;
std::unique_ptr<BrowserPaneFooter> footerArea;

void selectPane(int);
int selectedPane{0};

bool autoPreviewEnabled{true};
};
} // namespace scxt::ui::app::browser_ui

Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ add_library(${PROJECT_NAME} STATIC
selection/selection_manager.cpp

voice/voice.cpp
voice/preview_voice.cpp

patch_io/patch_io.cpp

Expand Down
13 changes: 13 additions & 0 deletions src/engine/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "feature_enums.h"
#include "missing_resolution.h"
#include "sst/voicemanager/midi1_to_voicemanager.h"
#include "voice/preview_voice.h"

namespace scxt::engine
{
Expand Down Expand Up @@ -133,6 +134,8 @@ Engine::Engine()
{
ep = std::make_unique<voice::modulation::MatrixEndpoints>(nullptr);
}

previewVoice = std::make_unique<voice::PreviewVoice>();
}

Engine::~Engine()
Expand Down Expand Up @@ -342,6 +345,15 @@ bool Engine::processAudio()

getPatch()->process(*this);

if (previewVoice->isActive)
{
previewVoice->processBlock();

auto &main = getPatch()->busses.mainBus.output;
mech::accumulate_from_to<blockSize>(previewVoice->output[0], main[0]);
mech::accumulate_from_to<blockSize>(previewVoice->output[1], main[1]);
}

auto &bl = sharedUIMemoryState.busVULevels;
const auto &bs = getPatch()->busses;
for (int c = 0; c < 2; ++c)
Expand Down Expand Up @@ -928,6 +940,7 @@ void Engine::loadSf2MultiSampleIntoSelectedPart(const fs::path &p)
void Engine::onSampleRateChanged()
{
patch->setSampleRate(sampleRate);
previewVoice->setSampleRate(sampleRate);

messageController->forceStatusUpdate = true;
}
Expand Down
7 changes: 6 additions & 1 deletion src/engine/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
namespace scxt::voice
{
struct Voice;
}
struct PreviewVoice;
} // namespace scxt::voice
namespace scxt::messaging
{
struct MessageController;
Expand Down Expand Up @@ -251,6 +252,8 @@ struct Engine : MoveableOnly<Engine>, SampleRateSupport
} monoVoiceManagerResponder{*this};
using voiceManager_t =
sst::voicemanager::VoiceManager<VMConfig, VoiceManagerResponder, MonoVoiceManagerResponder>;
static_assert(sst::voicemanager::constraints::ConstraintsChecker<
VMConfig, VoiceManagerResponder, MonoVoiceManagerResponder>::satisfies());
voiceManager_t voiceManager{voiceManagerResponder, monoVoiceManagerResponder};

void onSampleRateChanged() override;
Expand All @@ -270,6 +273,8 @@ struct Engine : MoveableOnly<Engine>, SampleRateSupport
void assertActiveVoiceCount();
std::atomic<uint32_t> activeVoices{0};

std::unique_ptr<voice::PreviewVoice> previewVoice;

const std::unique_ptr<messaging::MessageController> &getMessageController() const
{
return messageController;
Expand Down
15 changes: 8 additions & 7 deletions src/engine/group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,16 +402,17 @@ void Group::postZoneTraversalRemoveHandler()
/*
* Go backwards down the weak refs removing inactive ones
*/
assert(rescanWeakRefs);
assert(activeZones);
assert(activeZones >= rescanWeakRefs);
for (int i = activeZones - 1; i >= 0; --i)
if (activeZones != 0)
{
if (!activeZoneWeakRefs[i]->isActive())
for (int i = activeZones - 1; i >= 0; --i)
{
rescanWeakRefs--;
activeZoneWeakRefs[i] = activeZoneWeakRefs[activeZones - 1];
activeZones--;
if (!activeZoneWeakRefs[i]->isActive())
{
rescanWeakRefs--;
activeZoneWeakRefs[i] = activeZoneWeakRefs[activeZones - 1];
activeZones--;
}
}
}
assert(rescanWeakRefs == 0);
Expand Down
33 changes: 33 additions & 0 deletions src/messaging/client/browser_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "client_macros.h"
#include "filesystem/import.h"
#include "browser/browser.h"
#include "voice/preview_voice.h"

namespace scxt::messaging::client
{
Expand All @@ -48,6 +49,38 @@ inline void doAddBrowserDeviceLocation(const fs::path &p, const engine::Engine &
CLIENT_TO_SERIAL(AddBrowserDeviceLocation, c2s_add_browser_device_location, std::string,
doAddBrowserDeviceLocation(fs::path(fs::u8path(payload)), engine, cont));

using previewBrowserSamplePayload_t = std::tuple<bool, std::string>;
inline void doPreviewBrowserSample(const previewBrowserSamplePayload_t &p,
const engine::Engine &engine, MessageController &cont)
{
auto [startstop, pathString] = p;
auto path = fs::path(fs::u8path(pathString));
if (startstop)
{
SCLOG("Starting preview " << path.u8string());
auto sid = engine.getSampleManager()->loadSampleByPath(path);
if (sid.has_value())
{
auto smp = engine.getSampleManager()->getSample(*sid);
cont.scheduleAudioThreadCallback(
[smp](auto &eng) { eng.previewVoice->attachAndStart(smp); });
}
else
{
cont.reportErrorToClient("Unable to launch preview",
"Sample file preview load failed for " + path.u8string());
}
}
else
{
cont.scheduleAudioThreadCallback(
[](auto &eng) { eng.previewVoice->detatchAndStop(); },
[](const auto &e) { e.getSampleManager()->purgeUnreferencedSamples(); });
}
}
CLIENT_TO_SERIAL(PreviewBrowserSample, c2s_preview_browser_sample, previewBrowserSamplePayload_t,
doPreviewBrowserSample(payload, engine, cont));

SERIAL_TO_CLIENT(RefreshBrowser, s2c_refresh_browser, bool, onBrowserRefresh)

} // namespace scxt::messaging::client
Expand Down
2 changes: 2 additions & 0 deletions src/messaging/client/client_serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ enum ClientToSerializationMessagesIds
c2s_set_mixer_effect_storage,
c2s_set_mixer_send_storage,

// Browser functions
c2s_add_browser_device_location,
c2s_preview_browser_sample,

c2s_request_debug_action,

Expand Down
Loading

0 comments on commit 10a872a

Please sign in to comment.