More boring incremental equalizer UI work.

This commit is contained in:
casey langen 2018-12-18 22:24:08 -08:00
parent db37f5e5f0
commit 64691f8c83
12 changed files with 267 additions and 39 deletions

View File

@ -69,3 +69,12 @@ inline static std::string u8substr(const std::string& in, int offset, int len) {
return std::string(begin, it);
}
template<typename... Args>
static std::string u8fmt(const std::string& format, Args ... args) {
/* https://stackoverflow.com/a/26221725 */
size_t size = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; /* extra space for '\0' */
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args ...);
return std::string(buf.get(), buf.get() + size - 1); /* omit the '\0' */
}

View File

@ -33,8 +33,11 @@
//////////////////////////////////////////////////////////////////////////////
#include "EqualizerOverlay.h"
#include <cursespp/App.h>
#include <cursespp/Screen.h>
#include <cursespp/Scrollbar.h>
#include <core/plugin/PluginFactory.h>
#include <cursespp/SingleLineEntry.h>
using namespace cursespp;
using namespace musik::core;
@ -43,21 +46,66 @@ using namespace musik::cube;
static const std::string SUPEREQ_PLUGIN_GUID = "6f0ed53b-0f13-4220-9b0a-ca496b6421cc";
static std::string formatBandRow(const std::string& band, float value) {
return band + "khz";
}
static const size_t NO_INDEX = (size_t)-1;
static int calculateWidth() {
return (int)(0.8f * Screen::GetWidth());
static const std::vector<std::string> BANDS = {
"65", "92", "131", "185", "262",
"370", "523", "740", "1047", "1480",
"2093", "2960", "4186", "5920", "8372",
"11840", "16744"
};
static const int VERTICAL_PADDING = 2;
static const int MAX_HEIGHT = 8 + (int) BANDS.size();
static std::string formatBandRow(const std::string& band, float value) {
return u8fmt("%s khz (%0.2f)", band.c_str(), value);
}
EqualizerOverlay::EqualizerOverlay()
: OverlayBase() {
this->plugin = this->FindPlugin();
this->prefs = Preferences::ForPlugin(this->plugin->Name());
this->titleLabel = std::make_shared<TextLabel>();
this->titleLabel->SetText(_TSTR("equalizer_overlay_title"), text::AlignCenter);
this->titleLabel->SetBold(true);
this->enabledCb = std::make_shared<Checkbox>();
this->enabledCb->SetText(_TSTR("equalizer_overlay_enabled"));
this->enabledCb->SetChecked(this->prefs->GetBool("enabled", false));
this->adapter = std::make_shared<BandsAdapter>(prefs);
this->listView = std::make_shared<ListWindow>(this->adapter);
this->listView->SetFrameTitle(_TSTR("equalizer_overlay_frequencies"));
/* add */
this->AddWindow(this->titleLabel);
this->AddWindow(this->enabledCb);
this->AddWindow(this->listView);
/* focus */
int order = 0;
this->enabledCb->SetFocusOrder(order++);
this->listView->SetFocusOrder(order++);
/* style */
style(*this->titleLabel);
style(*this->enabledCb);
style(*this->listView);
this->listView->SetFrameVisible(true);
}
EqualizerOverlay::~EqualizerOverlay() {
}
void EqualizerOverlay::ShowOverlay() {
App::Overlays().Push(std::make_shared<EqualizerOverlay>());
}
bool EqualizerOverlay::CanScroll(int listViewHeight) {
return listViewHeight < this->adapter->GetEntryCount();
}
std::shared_ptr<IPlugin> EqualizerOverlay::FindPlugin() {
@ -72,3 +120,59 @@ std::shared_ptr<IPlugin> EqualizerOverlay::FindPlugin() {
});
return result;
}
void EqualizerOverlay::Layout() {
int width = (int)(0.8f * (float) Screen::GetWidth());
int height = std::min(Screen::GetHeight() - 4, MAX_HEIGHT);
int y = VERTICAL_PADDING;
int x = (Screen::GetWidth() / 2) - (width / 2);
this->MoveAndResize(x, y, width, height);
int cx = this->GetContentWidth();
int cy = this->GetContentHeight();
x = 0;
y = 0;
this->titleLabel->MoveAndResize(x, y, cx, 1);
y += 2;
this->enabledCb->MoveAndResize(x, y, cx, 1);
y += 2;
cy -= 4;
this->listView->MoveAndResize(x, y, cx, cy);
}
bool EqualizerOverlay::KeyPress(const std::string& key) {
if (key == "^[" || key == "ESC") { /* esc closes */
this->Dismiss();
return true;
}
return OverlayBase::KeyPress(key);
}
/* ~~~~~~ BandsAdapter ~~~~~ */
EqualizerOverlay::BandsAdapter::BandsAdapter(EqualizerOverlay::Prefs prefs)
: prefs(prefs) {
}
EqualizerOverlay::BandsAdapter::~BandsAdapter() {
}
size_t EqualizerOverlay::BandsAdapter::GetEntryCount() {
return BANDS.size();
}
ScrollAdapterBase::EntryPtr EqualizerOverlay::BandsAdapter::GetEntry(cursespp::ScrollableWindow* window, size_t index) {
const std::string band = BANDS[index];
auto entry = std::make_shared<SingleLineEntry>(
formatBandRow(band, prefs->GetDouble(band.c_str(), 1.0)));
entry->SetAttrs(CURSESPP_DEFAULT_COLOR);
if (index == window->GetScrollPosition().logicalIndex) {
entry->SetAttrs(COLOR_PAIR(CURSESPP_HIGHLIGHTED_LIST_ITEM));
}
return entry;
}

View File

@ -39,6 +39,7 @@
#include <cursespp/OverlayBase.h>
#include <cursespp/Checkbox.h>
#include <cursespp/ListWindow.h>
#include <cursespp/ScrollAdapterBase.h>
#include <sigslot/sigslot.h>
#include <memory>
@ -50,6 +51,12 @@ namespace musik {
#endif
{
public:
using Plugin = std::shared_ptr<musik::core::sdk::IPlugin>;
using Prefs = std::shared_ptr<musik::core::Preferences>;
EqualizerOverlay();
virtual ~EqualizerOverlay();
static void ShowOverlay();
static std::shared_ptr<musik::core::sdk::IPlugin> FindPlugin();
@ -57,10 +64,23 @@ namespace musik {
virtual bool KeyPress(const std::string& key) override;
private:
EqualizerOverlay();
class BandsAdapter : public cursespp::ScrollAdapterBase {
public:
BandsAdapter(Prefs prefs);
virtual ~BandsAdapter();
virtual size_t GetEntryCount() override;
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index) override;
std::shared_ptr<musik::core::sdk::IPlugin> plugin;
std::shared_ptr<musik::core::Preferences> prefs;
private:
Prefs prefs;
};
bool CanScroll(int listViewHeight);
Plugin plugin;
Prefs prefs;
std::shared_ptr<BandsAdapter> adapter;
std::shared_ptr<cursespp::TextLabel> titleLabel;
std::shared_ptr<cursespp::Checkbox> enabledCb;
std::shared_ptr<cursespp::ListWindow> listView;
};

View File

@ -168,19 +168,19 @@ void ServerOverlay::InitViews() {
this->pwInput.reset(new TextInput(TextInput::StyleLine, IInput::InputPassword));
/* style 'em */
applyLabelOverlayStyle(*this->titleLabel);
applyCheckboxOverlayStyle(*this->enableWssCb);
applyLabelOverlayStyle(*this->wssPortLabel);
applyInputOverlayStyle(*this->wssPortInput);
applyCheckboxOverlayStyle(*this->enableHttpCb);
applyLabelOverlayStyle(*this->httpPortLabel);
applyInputOverlayStyle(*this->httpPortInput);
applyCheckboxOverlayStyle(*this->ipv6Cb);
applyCheckboxOverlayStyle(*this->enableSyncTransCb);
applyLabelOverlayStyle(*this->transCacheLabel);
applyInputOverlayStyle(*this->transCacheInput);
applyLabelOverlayStyle(*this->pwLabel);
applyInputOverlayStyle(*this->pwInput);
style(*this->titleLabel);
style(*this->enableWssCb);
style(*this->wssPortLabel);
style(*this->wssPortInput);
style(*this->enableHttpCb);
style(*this->httpPortLabel);
style(*this->httpPortInput);
style(*this->ipv6Cb);
style(*this->enableSyncTransCb);
style(*this->transCacheLabel);
style(*this->transCacheInput);
style(*this->pwLabel);
style(*this->pwInput);
/* add 'em */
this->AddWindow(this->titleLabel);

View File

@ -66,12 +66,20 @@ void ListWindow::SetScrollbarVisible(bool visible) {
}
}
void ListWindow::SetDecorator(Decorator decorator) {
this->decorator = decorator;
}
void ListWindow::Invalidate() {
this->DecorateFrame();
Window::Invalidate();
}
void ListWindow::DecorateFrame() {
if (this->decorator) {
this->decorator(this);
}
if (this->IsFrameVisible()) {
Scrollbar::Draw(this);
}

View File

@ -38,6 +38,7 @@
#include "IScrollAdapter.h"
#include "ScrollableWindow.h"
#include <sigslot/sigslot.h>
#include <functional>
namespace cursespp {
class ListWindow :
@ -49,6 +50,8 @@ namespace cursespp {
public:
static size_t NO_SELECTION;
using Decorator = std::function<void(ListWindow*)>;
sigslot::signal3<ListWindow*, size_t, size_t> SelectionChanged;
sigslot::signal2<ListWindow*, size_t> Invalidated;
sigslot::signal2<ListWindow*, size_t> EntryActivated;
@ -79,6 +82,7 @@ namespace cursespp {
virtual bool MouseEvent(const IMouseHandler::Event& event);
void SetScrollbarVisible(bool visible);
void SetDecorator(Decorator decorator);
protected:
virtual void OnSelectionChanged(size_t newIndex, size_t oldIndex);
@ -95,5 +99,6 @@ namespace cursespp {
bool showScrollbar;
IScrollAdapter::ScrollPosition scrollPosition;
size_t selectedIndex;
Decorator decorator;
};
}

View File

@ -41,12 +41,15 @@
#include "TextLabel.h"
#include "Checkbox.h"
#include "TextInput.h"
#include "ListWindow.h"
namespace cursespp {
class OverlayBase : public LayoutBase, public IOverlay {
public:
OverlayBase() : LayoutBase() {
this->SetFrameVisible(true);
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
this->SetContentColor(CURSESPP_OVERLAY_CONTENT);
}
virtual ~OverlayBase() {
@ -72,16 +75,16 @@ namespace cursespp {
}
protected:
static void applyLabelOverlayStyle(TextLabel& label) {
static void style(TextLabel& label) {
label.SetContentColor(CURSESPP_OVERLAY_CONTENT);
}
static void applyCheckboxOverlayStyle(Checkbox& cb) {
static void style(Checkbox& cb) {
cb.SetContentColor(CURSESPP_OVERLAY_CONTENT);
cb.SetFocusedContentColor(CURSESPP_OVERLAY_TEXT_FOCUSED);
}
static void applyInputOverlayStyle(TextInput& input) {
static void style(TextInput& input) {
if (input.GetStyle() == TextInput::StyleBox) {
input.SetFrameColor(CURSESPP_OVERLAY_FRAME);
input.SetContentColor(CURSESPP_OVERLAY_CONTENT);
@ -94,6 +97,12 @@ namespace cursespp {
}
}
static void style(ListWindow& listWindow, bool frameVisible = false) {
listWindow.SetContentColor(CURSESPP_OVERLAY_CONTENT);
listWindow.SetFocusedContentColor(CURSESPP_OVERLAY_CONTENT);
listWindow.SetFrameVisible(frameVisible);
}
OverlayStack* GetOverlayStack() {
return this->stack;
}

View File

@ -162,6 +162,10 @@
"visualizer_overlay_title": "visualizers",
"visualizer_overlay_no_visualizers_message": "no visualizers found!",
"equalizer_overlay_title": "equalizer",
"equalizer_overlay_enabled": "equalizer enabled",
"equalizer_overlay_frequencies": "frequencies",
"plugin_overlay_title": "configure plugins",
"shortcuts_settings": "settings",

View File

@ -32,16 +32,35 @@
//
//////////////////////////////////////////////////////////////////////////////
#include "constants.h"
#include "SuperEqDsp.h"
#include <core/sdk/constants.h>
#include <core/sdk/IPreferences.h>
#include <core/sdk/ISchema.h>
#include <atomic>
using namespace musik::core::sdk;
SuperEqDsp::SuperEqDsp() {
static IPreferences* prefs = nullptr;
static std::atomic<int> currentState;
static const std::vector<std::string> BANDS = {
"65", "92", "131", "185", "262",
"370", "523", "740", "1047", "1480",
"2093", "2960", "4186", "5920", "8372",
"11840", "16744"
};
extern "C" DLLEXPORT void SetPreferences(IPreferences* prefs) {
::prefs = prefs;
}
void SuperEqDsp::NotifyChanged() {
currentState.fetch_add(1);
}
SuperEqDsp::SuperEqDsp() {
this->enabled = ::prefs && ::prefs->GetBool("enabled", false);
}
SuperEqDsp::~SuperEqDsp() {
@ -56,17 +75,25 @@ void SuperEqDsp::Release() {
}
bool SuperEqDsp::Process(IBuffer* buffer) {
int channels = buffer->Channels();
if (!this->enabled) {
return false;
}
int channels = buffer->Channels();
int current = ::currentState.load();
if (!this->supereq || this->lastUpdated != current) {
this->enabled = ::prefs && ::prefs->GetBool("enabled", false);
this->lastUpdated = current;
if (!this->supereq) {
this->supereq = new SuperEqState();
equ_init(this->supereq, 10, channels);
void *params = paramlist_alloc();
float bands[18];
float bands[17];
for (int i = 0; i < 18; i++) {
bands[i] = 1.0f; //i > 9 ? -0.0f : 1.0f;
for (int i = 0; i < BANDS.size(); i++) {
prefs->GetDouble(BANDS[i].c_str(), 1.0);
}
equ_makeTable(

View File

@ -46,6 +46,10 @@ class SuperEqDsp : public IDSP {
virtual void Release() override;
virtual bool Process(IBuffer *buffer) override;
static void NotifyChanged();
private:
SuperEqState* supereq {nullptr};
int lastUpdated {0};
bool enabled;
};

View File

@ -0,0 +1,43 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <atomic>
#ifdef WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif

View File

@ -32,18 +32,13 @@
//
//////////////////////////////////////////////////////////////////////////////
#include "constants.h"
#include <core/sdk/constants.h>
#include <core/sdk/IPlugin.h>
#include <core/sdk/ISchema.h>
#include <core/sdk/IDSP.h>
#include "SuperEqDsp.h"
#ifdef WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
class SuperEqPlugin : public musik::core::sdk::IPlugin {
public:
virtual void Release() { delete this; }
@ -53,7 +48,7 @@ class SuperEqPlugin : public musik::core::sdk::IPlugin {
virtual const char* Guid() { return "6f0ed53b-0f13-4220-9b0a-ca496b6421cc"; }
virtual bool Configurable() { return false; }
virtual void Configure() { }
virtual void Reload() { }
virtual void Reload() { SuperEqDsp::NotifyChanged(); }
virtual int SdkVersion() { return musik::core::sdk::SdkVersion; }
};