Add QT6 frontend
Some checks failed
Build / build-gentoo (push) Failing after 1m10s
Build / download-system-deps (push) Successful in 3m44s
Build / get-source-code (push) Successful in 7m3s
Build / build-appimage (push) Successful in 3m28s
Build / build-android (push) Failing after 2m54s
Build / build-windows (push) Failing after 6m34s
Some checks failed
Build / build-gentoo (push) Failing after 1m10s
Build / download-system-deps (push) Successful in 3m44s
Build / get-source-code (push) Successful in 7m3s
Build / build-appimage (push) Successful in 3m28s
Build / build-android (push) Failing after 2m54s
Build / build-windows (push) Failing after 6m34s
This commit is contained in:
parent
8fdf30878b
commit
f7f9fce3cb
28 changed files with 1323 additions and 44 deletions
2
.clangd
2
.clangd
|
@ -1,2 +0,0 @@
|
|||
CompileFlags:
|
||||
CompilationDatabase: "./build/compile_commands.json"
|
20
.vscode/c_cpp_properties.json
vendored
Normal file
20
.vscode/c_cpp_properties.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"/usr/include/qt6/QtCore",
|
||||
"/usr/include/qt6/QtGui",
|
||||
"/usr/include/qt6/QtWidgets"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.cmake-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
0
.zed/settings.json
Normal file
0
.zed/settings.json
Normal file
|
@ -102,8 +102,6 @@ if (DEFINED EMSCRIPTEN)
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_FLAGS}")
|
||||
set(EXTRA_LINKER_FLAGS "${EXTRA_LINKER_FLAGS} ${EXTRA_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EXTRA_LINKER_FLAGS}")
|
||||
else()
|
||||
set(BUILD_STATIC OFF CACHE BOOL "")
|
||||
endif()
|
||||
option(BUILD_SDL "Enables built-in SDL" OFF)
|
||||
option(BUILD_SDL_IMAGE "Enables built-in SDL_image" ${BUILD_SDL})
|
||||
|
@ -160,7 +158,11 @@ add_subdirectory(subprojects/vgmstream)
|
|||
if (DEFINED EMSCRIPTEN)
|
||||
set(EXTRA_LIBS )
|
||||
else()
|
||||
set(EXTRA_LIBS libvgmstream_shared)
|
||||
if (BUILD_STATIC)
|
||||
set(EXTRA_LIBS libvgmstream)
|
||||
else()
|
||||
set(EXTRA_LIBS libvgmstream_shared)
|
||||
endif()
|
||||
endif()
|
||||
if(SDL_MIXER_X_STATIC)
|
||||
set(SDL_MIXER_X_TARGET SDL2_mixer_ext_Static)
|
||||
|
@ -402,7 +404,7 @@ else()
|
|||
if (TARGET SDL2-static)
|
||||
set(SDL2_TARGET SDL2-static)
|
||||
endif()
|
||||
target_link_libraries(liblooper PUBLIC ${SDL2_TARGET} ${SDL_MIXER_X_TARGET} ${SOUNDTOUCH_TARGET} libvgmstream libvgmstream_shared ${JSONCPP_TARGET})
|
||||
target_link_libraries(liblooper PUBLIC ${SDL2_TARGET} ${SDL_MIXER_X_TARGET} ${SOUNDTOUCH_TARGET} libvgmstream ${JSONCPP_TARGET})
|
||||
endif()
|
||||
if(BUILD_PROTOBUF)
|
||||
add_subdirectory(subprojects/protobuf)
|
||||
|
@ -483,6 +485,7 @@ endmacro()
|
|||
set(ENABLED_UIS )
|
||||
set(ENABLED_PLAYBACK_BACKENDS )
|
||||
ui_backend_subdir(NAME "IMGUI" READABLE_NAME "Dear ImGui" SUBDIR backends/ui/imgui)
|
||||
ui_backend_subdir(NAME "QT" READABLE_NAME "Qt" SUBDIR backends/ui/qt)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Haiku")
|
||||
ui_backend_subdir(NAME "HAIKU" READABLE_NAME "Haiku Native" SUBDIR backends/ui/haiku)
|
||||
endif()
|
||||
|
@ -537,7 +540,7 @@ if(DEFINED EMSCRIPTEN)
|
|||
copy_to_bindir(assets/ForkAwesome/css/fork-awesome.min.css fork-awesome.min.css)
|
||||
copy_to_bindir(assets/ForkAwesome/css/fork-awesome.min.css.map fork-awesome.min.css.map)
|
||||
endif()
|
||||
target_link_libraries(${TARGET_NAME} PUBLIC liblooper ${UI_BACKENDS} ${PLAYBACK_BACKENDS})
|
||||
target_link_libraries(${TARGET_NAME} PUBLIC liblooper ${UI_BACKENDS} ${PLAYBACK_BACKENDS} vorbis mpg123)
|
||||
install(TARGETS ${TARGET_NAME} ${EXTRA_LIBS})
|
||||
if (${BUILD_SDL2})
|
||||
install(EXPORT SDL2-static SDL2main)
|
||||
|
|
9
audio_output_backend.hpp
Normal file
9
audio_output_backend.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "backend.hpp"
|
||||
class AudioOutputBackend {
|
||||
BACKEND_TYPE(AudioOutputBackend);
|
||||
protected:
|
||||
|
||||
};
|
|
@ -40,7 +40,7 @@ std::vector<Subwindow*> Subwindow::windows;
|
|||
BMessage *make_slider_msg(uint32_t what, bool down) {
|
||||
BMessage *msg = new BMessage(what);
|
||||
msg->SetBool(CMD_MOUSE_DOWN_KEY, down);
|
||||
return msg;
|
||||
return msg;
|
||||
}
|
||||
bool is_slider_down_msg(BMessage *msg) {
|
||||
return msg->HasBool(CMD_MOUSE_DOWN_KEY) && msg->GetBool(CMD_MOUSE_DOWN_KEY);
|
||||
|
@ -186,7 +186,7 @@ void LooperWindow::MessageReceived(BMessage *msg) {
|
|||
default: {
|
||||
msg->PrintToStream();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (msg->what) {
|
||||
|
@ -263,7 +263,7 @@ void LooperWindow::MessageReceived(BMessage *msg) {
|
|||
WARNING.writefln("entry.GetPath(&path) == %d", (int32)(err));
|
||||
msg->PrintToStream();
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
WARNING.writefln("msg->FindRef(\"refs\", &ref) == %d", (int32)(err));
|
||||
msg->PrintToStream();
|
||||
}
|
||||
|
@ -279,23 +279,6 @@ void LooperWindow::MessageReceived(BMessage *msg) {
|
|||
} break;
|
||||
};
|
||||
}
|
||||
LooperLogScaler::LooperLogScaler(double min, double max) {
|
||||
update_min_max(min, max);
|
||||
}
|
||||
void LooperLogScaler::update_min_max(double min, double max) {
|
||||
x0 = min;
|
||||
x1 = (max - min) / (exp(1.0) - 1.0);
|
||||
la = min;
|
||||
lb = (max - min);
|
||||
// x0 = scale_log(min);
|
||||
// x1 = scale_log(max);
|
||||
}
|
||||
double LooperLogScaler::scale_log(double value) {
|
||||
return (std::log(((value - x0) / x1) + 1.0) * lb) + la;
|
||||
}
|
||||
double LooperLogScaler::unscale_log(double value) {
|
||||
return ((std::exp((value - la) / lb) - 1.0) * x1) + x0;
|
||||
}
|
||||
void LooperWindow::Pulse() {
|
||||
auto len = playback->GetLength();
|
||||
auto pos = playback->GetPosition();
|
||||
|
@ -324,7 +307,7 @@ void LooperWindow::Pulse() {
|
|||
auto speed = playback->GetSpeed();
|
||||
auto tempo = playback->GetTempo();
|
||||
if (!volume_clicked) volume_slider->SetValue(volume);
|
||||
volume_slider->SetLabel(fmt::format("Volume: {}%", (uint8_t)volume).c_str());
|
||||
volume_slider->SetLabel(fmt::format("Volume: {}%", (int)volume).c_str());
|
||||
if (!pitch_clicked) pitch_slider->SetValueDouble(pitch);
|
||||
pitch_slider->SetLabel(fmt::format("Pitch {:.02f}x", pitch).c_str());
|
||||
if (!speed_clicked) speed_slider->SetValueDouble(speed);
|
||||
|
|
|
@ -14,18 +14,8 @@
|
|||
#include "slider.h"
|
||||
#include "prefs.h"
|
||||
#include <atomic>
|
||||
#include <util.hpp>
|
||||
#define CMD_UPDATE_LABEL_SETTING 0x1000
|
||||
class LooperLogScaler {
|
||||
double la;
|
||||
double lb;
|
||||
public:
|
||||
double x0;
|
||||
double x1;
|
||||
void update_min_max(double min, double max);
|
||||
LooperLogScaler(double min, double max);
|
||||
double scale_log(double value);
|
||||
double unscale_log(double value);
|
||||
};
|
||||
extern bool show_labels;
|
||||
extern bool show_icons;
|
||||
extern bool quitting;
|
||||
|
@ -36,7 +26,7 @@ class Subwindow {
|
|||
std::atomic_bool Showing = false;
|
||||
std::atomic_bool ShownEver = false;
|
||||
private:
|
||||
inline Subwindow(BWindow *window)
|
||||
inline Subwindow(BWindow *window)
|
||||
: window(window)
|
||||
{ }
|
||||
public:
|
||||
|
@ -98,5 +88,5 @@ class LooperRefHandler : public BHandler {
|
|||
BHandler *next_handler;
|
||||
public:
|
||||
void MessageReceived(BMessage *msg) override;
|
||||
LooperRefHandler(LooperWindow *win);
|
||||
LooperRefHandler(LooperWindow *win);
|
||||
};
|
||||
|
|
|
@ -107,7 +107,7 @@ void PrefsWindow::MessageReceived(BMessage *msg) {
|
|||
case CMD_REVERT: {
|
||||
set_options_changed(false);
|
||||
new_label_setting = get_option<std::string>("ui.haiku.label_setting", "icons");
|
||||
new_frontend = get_option<std::string>("ui.frontend", "haiki");
|
||||
new_frontend = get_option<std::string>("ui.frontend", "haiku");
|
||||
if (new_frontend != "haiku") restart_warning->Show();
|
||||
else restart_warning->Hide();
|
||||
for (size_t i = 0; i < backend_ids.size(); i++) {
|
||||
|
|
13
backends/ui/qt/CMakeLists.txt
Normal file
13
backends/ui/qt/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
set(BACKEND_QT_SRC_BASE main.cpp main.h main_window.cpp main_window.h slider.hpp slider.cpp preferences.h preferences.cpp aboutwindow.h aboutwindow.cpp)
|
||||
set(BACKEND_QT_SRC )
|
||||
foreach(SRC IN ITEMS ${BACKEND_QT_SRC_BASE})
|
||||
set(BACKEND_QT_SRC ${BACKEND_QT_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/${SRC})
|
||||
endforeach()
|
||||
set(BACKEND_QT_INC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
add_ui_backend(qt_ui ${BACKEND_QT_SRC})
|
||||
find_package(Qt6 COMPONENTS Core Gui Widgets)
|
||||
target_link_libraries(qt_ui PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets SDL2::SDL2 fmt::fmt liblooper)
|
||||
target_include_directories(qt_ui PRIVATE ../../..)
|
||||
#target_link_libraries(qt_ui PRIVATE PkgConfig::GTK4 PkgConfig::gtkmm4)
|
||||
#target_include_directories(qt_ui PRIVATE ${BACKEND_QT_INC})
|
66
backends/ui/qt/aboutwindow.cpp
Normal file
66
backends/ui/qt/aboutwindow.cpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#include "aboutwindow.h"
|
||||
QModelIndex LicenseModel::index(int row, int column, const QModelIndex &parent) const {
|
||||
return createIndex(row, 0, row);
|
||||
}
|
||||
QModelIndex LicenseModel::parent(const QModelIndex &child) const {
|
||||
return QModelIndex();
|
||||
}
|
||||
Qt::ItemFlags LicenseModel::flags(const QModelIndex &index) const {
|
||||
return Qt::ItemFlag::ItemIsEnabled|Qt::ItemFlag::ItemIsSelectable;
|
||||
}
|
||||
int LicenseModel::rowCount(const QModelIndex &parent) const {
|
||||
return parent.isValid() ? 0 : licenseData.size();
|
||||
}
|
||||
int LicenseModel::columnCount(const QModelIndex &parent) const {
|
||||
return parent.isValid() ? 0 : 1;
|
||||
}
|
||||
QVariant LicenseModel::data(const QModelIndex &index, int role) const {
|
||||
if ((index.row() < 0 || licenseData.size() <= index.row()) && (role == Qt::DisplayRole || role == Qt::UserRole)) {
|
||||
return "";
|
||||
}
|
||||
switch (role) {
|
||||
case Qt::DisplayRole: {
|
||||
const LicenseData &data = licenseData[index.row()];
|
||||
return QString::asprintf("%s (%s)", data.Project.c_str(), data.Spdx.c_str());
|
||||
} break;
|
||||
case Qt::UserRole: {
|
||||
return QString(licenseData[index.row()].LicenseContents.c_str());
|
||||
} break;
|
||||
default: {
|
||||
return QVariant();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
LicenseModel::LicenseModel() {
|
||||
auto tmp = get_license_data();
|
||||
for (auto data : tmp) {
|
||||
licenseData.push_back(data);
|
||||
}
|
||||
}
|
||||
LicenseModel::~LicenseModel() {
|
||||
|
||||
}
|
||||
AboutWindow::AboutWindow() {
|
||||
license_text = new QTextBrowser();
|
||||
license_list = new QListView();
|
||||
license_list->setModel(new LicenseModel());
|
||||
QObject::connect(license_list, &QListView::clicked, [=,this](const QModelIndex &idx) {
|
||||
license_text->setText(license_list->model()->data(idx, Qt::UserRole).toString());
|
||||
});
|
||||
license_list->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
license_list->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
QLabel *title = new QLabel("Looper");
|
||||
auto font = title->font();
|
||||
font.setPointSize(24);
|
||||
title->setFont(font);
|
||||
mainLayout->addWidget(title);
|
||||
QLabel *versionText = new QLabel(TAG);
|
||||
mainLayout->addWidget(versionText);
|
||||
QSplitter *splitter = new QSplitter(Qt::Orientation::Horizontal);
|
||||
splitter->addWidget(license_list);
|
||||
splitter->addWidget(license_text);
|
||||
mainLayout->addWidget(splitter);
|
||||
setLayout(mainLayout);
|
||||
license_list->clicked(license_list->model()->index(0, 0));
|
||||
}
|
33
backends/ui/qt/aboutwindow.h
Normal file
33
backends/ui/qt/aboutwindow.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QTextBrowser>
|
||||
#include <string>
|
||||
#include <QListView>
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
#include <QBoxLayout>
|
||||
#include <QSplitter>
|
||||
#include <QLabel>
|
||||
#include <license.hpp>
|
||||
|
||||
class LicenseModel : public QAbstractListModel {
|
||||
Q_OBJECT;
|
||||
std::vector<LicenseData> licenseData;
|
||||
public:
|
||||
~LicenseModel() override;
|
||||
Q_INVOKABLE Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
Q_INVOKABLE QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||
Q_INVOKABLE QModelIndex parent(const QModelIndex &child) const override;
|
||||
Q_INVOKABLE int rowCount(const QModelIndex &parent) const override;
|
||||
Q_INVOKABLE int columnCount(const QModelIndex &parent) const override;
|
||||
Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override;
|
||||
LicenseModel();
|
||||
};
|
||||
class AboutWindow : public QWidget {
|
||||
Q_OBJECT;
|
||||
public:
|
||||
QListView *license_list;
|
||||
QTextBrowser *license_text;
|
||||
public:
|
||||
AboutWindow();
|
||||
};
|
19
backends/ui/qt/main.cpp
Normal file
19
backends/ui/qt/main.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include "main.h"
|
||||
#include "main_window.h"
|
||||
#include <QMainWindow>
|
||||
#include <QWindow>
|
||||
#include <QGuiApplication>
|
||||
#include <QApplication>
|
||||
std::string QtUIBackend::get_id() {
|
||||
return "qt";
|
||||
}
|
||||
std::string QtUIBackend::get_name() {
|
||||
return "QT";
|
||||
}
|
||||
int QtUIBackend::run(std::vector<std::string> args, int argc, char **argv) {
|
||||
UIBackend::run(args, argc, argv);
|
||||
QApplication app(argc, argv);
|
||||
LooperWindow window(this->playback);
|
||||
window.show();
|
||||
return app.exec();
|
||||
}
|
10
backends/ui/qt/main.h
Normal file
10
backends/ui/qt/main.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
#include <backend.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
class QtUIBackend : public UIBackend {
|
||||
public:
|
||||
std::string get_id() override;
|
||||
std::string get_name() override;
|
||||
int run(std::vector<std::string> args, int argc, char **argv) override;
|
||||
};
|
159
backends/ui/qt/main_window.cpp
Normal file
159
backends/ui/qt/main_window.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
#include "main_window.h"
|
||||
#include <QMenuBar>
|
||||
#include "preferences.h"
|
||||
void LooperWindow::Pulse() {
|
||||
auto len = playback->GetLength();
|
||||
auto pos = playback->GetPosition();
|
||||
this->slider->SetLimits(0.0, len);
|
||||
this->slider->SetValue(pos);
|
||||
auto component_count = TimeToComponentCount(len);
|
||||
bool enable_ui = !playback->IsStopped();
|
||||
if (enable_ui) {
|
||||
slider->SetLabel(fmt::format("Position: {}", TimeToString(pos, component_count)).c_str());
|
||||
slider->SetLimitLabels(TimeToString(0, component_count).c_str(), TimeToString(len).c_str());
|
||||
} else {
|
||||
slider->SetLabel("Position");
|
||||
slider->SetLimitLabels("N/A", "N/A");
|
||||
}
|
||||
update_label_setting(labels_visible, icons_visible);
|
||||
slider->setEnabled(enable_ui);
|
||||
auto volume = playback->GetVolume();
|
||||
auto pitch = playback->GetPitch();
|
||||
auto speed = playback->GetSpeed();
|
||||
auto tempo = playback->GetTempo();
|
||||
volume_slider->SetValue(volume);
|
||||
pitch_slider->SetValue(pitch);
|
||||
speed_slider->SetValue(speed);
|
||||
tempo_slider->SetValue(tempo);
|
||||
volume_slider->SetLabel(fmt::format("Volume: {}%", (int)volume).c_str());
|
||||
pitch_slider->SetLabel(fmt::format("Pitch {:.02f}x", pitch).c_str());
|
||||
speed_slider->SetLabel(fmt::format("Speed: {:.02f}x", speed).c_str());
|
||||
tempo_slider->SetLabel(fmt::format("Tempo: {:.02f}x", tempo).c_str());
|
||||
}
|
||||
LooperWindow::LooperWindow(Playback *playback) : QMainWindow() {
|
||||
labels_visible = false;
|
||||
icons_visible = true;
|
||||
this->playback = playback;
|
||||
this->root_layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
QWidget *central_widget = new QWidget();
|
||||
central_widget->setLayout(this->root_layout);
|
||||
this->setCentralWidget(central_widget);
|
||||
prefs_window = new PrefsWindow();
|
||||
about_window = new AboutWindow();
|
||||
QMenuBar *bar = this->menuBar();
|
||||
file_menu = new QMenu("File");
|
||||
open_item = new QAction("Open...");
|
||||
file_dialog = new QFileDialog(this);
|
||||
QObject::connect(file_dialog, &QFileDialog::fileSelected, [=,this](const QString &file) {
|
||||
playback->Start(file.toUtf8().constData());
|
||||
});
|
||||
QObject::connect(open_item, &QAction::triggered, [=,this]() {
|
||||
file_dialog->show();
|
||||
});
|
||||
prefs_item = new QAction("Preferences...");
|
||||
QObject::connect(prefs_item, &QAction::triggered, [=,this]() {
|
||||
prefs_window->show();
|
||||
});
|
||||
quit_item = new QAction("Quit");
|
||||
QObject::connect(quit_item, &QAction::triggered, [=,this]() {
|
||||
qApp->quit();
|
||||
});
|
||||
file_menu->addAction(open_item);
|
||||
file_menu->addAction(prefs_item);
|
||||
file_menu->addAction(quit_item);
|
||||
help_menu = new QMenu("Help");
|
||||
about_item = new QAction("About...");
|
||||
QObject::connect(about_item, &QAction::triggered, [=,this]() {
|
||||
about_window->show();
|
||||
});
|
||||
help_menu->addAction(about_item);
|
||||
bar->addMenu(file_menu);
|
||||
bar->addMenu(help_menu);
|
||||
root_layout->addWidget(bar);
|
||||
QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
|
||||
root_layout->addSpacerItem(spacer);
|
||||
QBoxLayout *top_row = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
pause_resume_btn = new QPushButton("Pause");
|
||||
QObject::connect(pause_resume_btn, &QPushButton::pressed, [=,this]() {
|
||||
playback->Pause();
|
||||
});
|
||||
restart_btn = new QPushButton("Restart");
|
||||
QObject::connect(restart_btn, &QPushButton::pressed, [=,this]() {
|
||||
playback->Seek(0.0);
|
||||
});
|
||||
top_row->addWidget(pause_resume_btn);
|
||||
top_row->addWidget(restart_btn);
|
||||
slider = new LooperSlider("seek", "Position", 0.0, 1.0, 0.00000000001, false);
|
||||
QObject::connect(slider, &LooperSlider::changed, [=,this](double value) {
|
||||
playback->Seek(value);
|
||||
});
|
||||
top_row->addWidget(slider);
|
||||
stop_btn = new QPushButton("Stop");
|
||||
QObject::connect(stop_btn, &QPushButton::pressed, [=,this]() {
|
||||
playback->Stop();
|
||||
});
|
||||
top_row->addWidget(stop_btn);
|
||||
volume_slider = new LooperSlider("volume", "Volume", 0.0, 100.0, 1.0, false);
|
||||
volume_slider->SetLimitLabels("Muted", "Full Volume");
|
||||
top_row->addWidget(volume_slider);
|
||||
QWidget *top_row_widget = new QWidget();
|
||||
top_row_widget->setLayout(top_row);
|
||||
root_layout->addWidget(top_row_widget);
|
||||
QBoxLayout *bottom_row = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
speed_slider = new LooperSlider("speed", "Speed", 0.25, 4.0, 0.01, true);
|
||||
pitch_slider = new LooperSlider("pitch", "Pitch", 0.25, 4.0, 0.01, true);
|
||||
tempo_slider = new LooperSlider("tempo", "Tempo", 0.25, 4.0, 0.01, true);
|
||||
speed_slider->SetLimitLabels("0.25x", "4.00x");
|
||||
pitch_slider->SetLimitLabels("0.25x", "4.00x");
|
||||
tempo_slider->SetLimitLabels("0.25x", "4.00x");
|
||||
QObject::connect(speed_slider, &LooperSlider::changed, [=,this](double value) {
|
||||
playback->SetSpeed(value);
|
||||
});
|
||||
QObject::connect(pitch_slider, &LooperSlider::changed, [=,this](double value) {
|
||||
playback->SetPitch(value);
|
||||
});
|
||||
QObject::connect(tempo_slider, &LooperSlider::changed, [=,this](double value) {
|
||||
playback->SetTempo(value);
|
||||
});
|
||||
bottom_row->addWidget(speed_slider);
|
||||
bottom_row->addWidget(pitch_slider);
|
||||
bottom_row->addWidget(tempo_slider);
|
||||
QWidget *bottom_row_widget = new QWidget();
|
||||
bottom_row_widget->setLayout(bottom_row);
|
||||
root_layout->addWidget(bottom_row_widget);
|
||||
QTimer *timer = new QTimer(this);
|
||||
QObject::connect(timer, &QTimer::timeout, [=,this]() {
|
||||
Pulse();
|
||||
});
|
||||
timer->setTimerType(Qt::TimerType::PreciseTimer);
|
||||
timer->setSingleShot(false);
|
||||
timer->setInterval(1);
|
||||
timer->start();
|
||||
QObject::connect(prefs_window, &PrefsWindow::settings_changed, this, &LooperWindow::update_label_setting);
|
||||
//setLayout(layout);
|
||||
}
|
||||
void LooperWindow::update_label_setting(bool labels_visible, bool icons_visible) {
|
||||
this->labels_visible = labels_visible;
|
||||
this->icons_visible = icons_visible;
|
||||
for (auto *slider : {volume_slider, speed_slider, tempo_slider, pitch_slider, slider}) {
|
||||
slider->SetShowButtonIcon(icons_visible);
|
||||
slider->SetShowButtonText(labels_visible);
|
||||
}
|
||||
QString strings[] = {"Restart", "Stop", (playback->IsPaused() && !playback->IsStopped()) ? "Resume" : "Pause"};
|
||||
QIcon icons[] = {QIcon::fromTheme("view-refresh"), QIcon::fromTheme("media-playback-stop"), QIcon::fromTheme((playback->IsPaused() && !playback->IsStopped()) ? "media-playback-start" : "media-playback-pause")};
|
||||
QPushButton *buttons[] = {restart_btn, stop_btn, pause_resume_btn};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
QIcon icon = icons[i];
|
||||
if (icon.isNull() || labels_visible) {
|
||||
buttons[i]->setText(strings[i]);
|
||||
} else {
|
||||
buttons[i]->setText("");
|
||||
}
|
||||
if (icons_visible && !icon.isNull()) {
|
||||
buttons[i]->setIcon(icons[i]);
|
||||
} else {
|
||||
QIcon emptyIcon;
|
||||
buttons[i]->setIcon(emptyIcon);
|
||||
}
|
||||
}
|
||||
}
|
49
backends/ui/qt/main_window.h
Normal file
49
backends/ui/qt/main_window.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
#include "playback.h"
|
||||
#include "util.hpp"
|
||||
#include <QMainWindow>
|
||||
#include <QSlider>
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QBoxLayout>
|
||||
#include <QMenu>
|
||||
#include <QTimer>
|
||||
#include <QFileDialog>
|
||||
#include <QApplication>
|
||||
#include <util.hpp>
|
||||
#include "preferences.h"
|
||||
#include "aboutwindow.h"
|
||||
#include "slider.hpp"
|
||||
class LooperWindow : public QMainWindow {
|
||||
Q_OBJECT;
|
||||
bool labels_visible;
|
||||
bool icons_visible;
|
||||
LooperSlider *volume_slider;
|
||||
LooperSlider *speed_slider;
|
||||
LooperSlider *tempo_slider;
|
||||
LooperSlider *pitch_slider;
|
||||
std::thread *update_thread = nullptr;
|
||||
bool done = false;
|
||||
void Pulse();
|
||||
void ThreadFunc();
|
||||
const char *file_to_play = nullptr;
|
||||
QPushButton *restart_btn;
|
||||
QPushButton *stop_btn;
|
||||
QPushButton *pause_resume_btn;
|
||||
Playback *playback;
|
||||
LooperSlider *slider;
|
||||
QMenu *file_menu;
|
||||
QAction *open_item;
|
||||
QAction *prefs_item;
|
||||
QAction *quit_item;
|
||||
QMenu *help_menu;
|
||||
QAction *about_item;
|
||||
QFileDialog *file_dialog;
|
||||
QBoxLayout *root_layout;
|
||||
void update_label_setting(bool labels_visible, bool icons_visible);
|
||||
public:
|
||||
AboutWindow *about_window;
|
||||
PrefsWindow *prefs_window;
|
||||
explicit LooperWindow(Playback *playback);
|
||||
};
|
95
backends/ui/qt/preferences.cpp
Normal file
95
backends/ui/qt/preferences.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
#include "preferences.h"
|
||||
#include <QMenu>
|
||||
#include <QBoxLayout>
|
||||
#include <backend.hpp>
|
||||
#include <options.hpp>
|
||||
using namespace Looper::Options;
|
||||
PrefsWindow::PrefsWindow() {
|
||||
auto *root_layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
this->setLayout(root_layout);
|
||||
restart_warning = new QLabel("A restart is needed to apply some changes.");
|
||||
restart_warning->hide();
|
||||
root_layout->addWidget(restart_warning);
|
||||
frontend_btn = new QPushButton();
|
||||
frontend_menu = new QMenu();
|
||||
for (auto &kv : UIBackend::backends) {
|
||||
UIBackend *backend = kv.second;
|
||||
const char *name = strdup(backend->get_name().c_str());
|
||||
QAction *action = new QAction(name);
|
||||
action->connect(action, &QAction::triggered, [=,this]() {
|
||||
this->new_frontend = backend->get_id();
|
||||
frontend_btn->setText(strdup(this->new_frontend.c_str()));
|
||||
this->set_options_changed(true);
|
||||
});
|
||||
frontend_menu->addAction(action);
|
||||
frontend_options.push_back(action);
|
||||
}
|
||||
frontend_btn->setMenu(frontend_menu);
|
||||
root_layout->addWidget(frontend_btn);
|
||||
QFrame *frame = new QFrame();
|
||||
frame->setWindowTitle("Labels and Icons");
|
||||
auto *label_settings_group = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
frame->setLayout(label_settings_group);
|
||||
labels_only = new QRadioButton("Labels Only");
|
||||
labels_only->connect(labels_only, &QRadioButton::pressed, [=,this]() {
|
||||
this->new_label_setting = "labels";
|
||||
this->set_options_changed(true);
|
||||
});
|
||||
icons_only = new QRadioButton("Icons Only");
|
||||
icons_only->connect(icons_only, &QRadioButton::pressed, [=,this]() {
|
||||
this->new_label_setting = "icons";
|
||||
this->set_options_changed(true);
|
||||
});
|
||||
both_labels_icons = new QRadioButton("Both");
|
||||
both_labels_icons->connect(both_labels_icons, &QRadioButton::pressed, [=,this]() {
|
||||
this->new_label_setting = "both";
|
||||
this->set_options_changed(true);
|
||||
});
|
||||
label_settings_group->addWidget(labels_only);
|
||||
label_settings_group->addWidget(icons_only);
|
||||
label_settings_group->addWidget(both_labels_icons);
|
||||
root_layout->addWidget(frame);
|
||||
revert_btn = new QPushButton("Revert");
|
||||
QObject::connect(revert_btn, &QPushButton::pressed, this, &PrefsWindow::revert);
|
||||
apply_btn = new QPushButton("Apply");
|
||||
QObject::connect(apply_btn, &QPushButton::pressed, this, &PrefsWindow::apply);
|
||||
QWidget *btn_view = new QWidget();
|
||||
QBoxLayout *btn_box = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
btn_view->setLayout(btn_box);
|
||||
btn_box->addWidget(revert_btn);
|
||||
btn_box->addWidget(apply_btn);
|
||||
root_layout->addWidget(btn_view);
|
||||
revert();
|
||||
}
|
||||
void PrefsWindow::set_options_changed(bool changed) {
|
||||
this->revert_btn->setEnabled(changed);
|
||||
this->apply_btn->setEnabled(changed);
|
||||
}
|
||||
void PrefsWindow::update_label_setting() {
|
||||
bool labels_enabled = true;
|
||||
bool icons_enabled = true;
|
||||
if (new_label_setting == "icons") {
|
||||
labels_enabled = false;
|
||||
} else if (new_label_setting == "labels") {
|
||||
icons_enabled = false;
|
||||
}
|
||||
emit(settings_changed(labels_enabled, icons_enabled));
|
||||
}
|
||||
void PrefsWindow::revert() {
|
||||
set_options_changed(false);
|
||||
new_label_setting = get_option<std::string>("ui.label_setting", "icons");
|
||||
new_frontend = get_option<std::string>("ui.frontend", "qt");
|
||||
if (new_frontend != "qt") restart_warning->show();
|
||||
else restart_warning->hide();
|
||||
frontend_btn->setText(new_frontend.c_str());
|
||||
update_label_setting();
|
||||
}
|
||||
void PrefsWindow::apply() {
|
||||
set_options_changed(false);
|
||||
set_option<std::string>("ui.label_setting", new_label_setting);
|
||||
set_option<std::string>("ui.frontend", new_frontend);
|
||||
if (new_frontend != "qt") restart_warning->show();
|
||||
else restart_warning->hide();
|
||||
frontend_btn->setText(new_frontend.c_str());
|
||||
update_label_setting();
|
||||
}
|
35
backends/ui/qt/preferences.h
Normal file
35
backends/ui/qt/preferences.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include <QWindow>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QRadioButton>
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
class PrefsWindow : public QWidget {
|
||||
Q_OBJECT;
|
||||
std::vector<std::string> backend_ids;
|
||||
int cur_option = 0;
|
||||
QLabel *restart_warning;
|
||||
std::string new_label_setting;
|
||||
std::string new_frontend;
|
||||
QPushButton *frontend_btn;
|
||||
QMenu *frontend_menu;
|
||||
std::vector<QAction*> frontend_options;
|
||||
QCheckBox *menu_icons;
|
||||
QRadioButton *labels_only;
|
||||
QRadioButton *icons_only;
|
||||
QRadioButton *both_labels_icons;
|
||||
QPushButton *revert_btn;
|
||||
QPushButton *apply_btn;
|
||||
void update_label_setting();
|
||||
void set_options_changed(bool changed);
|
||||
void revert();
|
||||
void apply();
|
||||
public:
|
||||
PrefsWindow();
|
||||
Q_SIGNALS:
|
||||
void settings_changed(bool use_labels, bool use_icons);
|
||||
};
|
249
backends/ui/qt/slider.cpp
Normal file
249
backends/ui/qt/slider.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
#include "slider.hpp"
|
||||
LooperSlider::LooperSlider(const char *name, const char *label, double min, double max, double tick, bool logarithmic) : QWidget() {
|
||||
root_layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
|
||||
text_layout_view = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
QWidget *text_layout_widget = new QWidget();
|
||||
text_layout_widget->setLayout(text_layout_view);
|
||||
root_layout->addWidget(text_layout_widget);
|
||||
text_label = new QLabel();
|
||||
slider = new QSlider(Qt::Orientation::Horizontal);
|
||||
slider->connect(slider, &QSlider::valueChanged, [=,this](int value) {
|
||||
slider_changed = 3;
|
||||
if (this->logarithmic) {
|
||||
this->SetValue(scaler->unscale_log(value * tick));
|
||||
} else {
|
||||
this->SetValue(value * tick);
|
||||
}
|
||||
});
|
||||
text_layout_view->addWidget(slider);
|
||||
btn = new QPushButton();
|
||||
btn->connect(btn, &QPushButton::pressed, [=,this]() {
|
||||
this->text_edit_mode = !this->text_edit_mode;
|
||||
UpdateSlider(true);
|
||||
});
|
||||
root_layout->addWidget(btn);
|
||||
text = new QLineEdit();
|
||||
text->setVisible(false);
|
||||
text_layout_view->addWidget(text);
|
||||
text->connect(text, &QLineEdit::textChanged, [=,this]() {
|
||||
const char *value = text->text().toLocal8Bit().constData();
|
||||
bool ok = true;
|
||||
double output = this->Value();
|
||||
try {
|
||||
output = std::stod(value);
|
||||
} catch (std::exception) {
|
||||
ok = false;
|
||||
}
|
||||
if (ok) {
|
||||
textChanged = true;
|
||||
this->SetValue(output);
|
||||
}
|
||||
});
|
||||
limits_view = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
min_label_view = new QLabel();
|
||||
limits_view->addWidget(min_label_view, 0, Qt::Alignment::enum_type::AlignLeft);
|
||||
max_label_view = new QLabel();
|
||||
limits_view->addWidget(max_label_view, 0, Qt::Alignment::enum_type::AlignRight);
|
||||
scaler = new LooperLogScaler(min, max);
|
||||
set_min(min);
|
||||
set_max(max);
|
||||
set_tick(tick);
|
||||
set_logarithmic(logarithmic);
|
||||
this->label = strdup(label);
|
||||
this->value = min;
|
||||
UpdateSlider(true);
|
||||
}
|
||||
double LooperSlider::Value() {
|
||||
return value;
|
||||
}
|
||||
void LooperSlider::SetValue(double value) {
|
||||
emit(changed(value));
|
||||
this->value = value;
|
||||
this->UpdateSlider(true);
|
||||
}
|
||||
void LooperSlider::set_min(double min) {
|
||||
settings_changed = true;
|
||||
this->min = min;
|
||||
}
|
||||
void LooperSlider::set_max(double max) {
|
||||
settings_changed = true;
|
||||
this->max = max;
|
||||
}
|
||||
void LooperSlider::set_logarithmic(bool logarithmic) {
|
||||
settings_changed = true;
|
||||
this->logarithmic = true;
|
||||
}
|
||||
void LooperSlider::UpdateLogScaler() {
|
||||
this->scaler->update_min_max(this->min, this->max);
|
||||
}
|
||||
void LooperSlider::SetMin(double min) {
|
||||
this->set_min(min);
|
||||
UpdateSlider(false);
|
||||
}
|
||||
void LooperSlider::SetMax(double max) {
|
||||
this->set_max(max);
|
||||
UpdateSlider(false);
|
||||
}
|
||||
double LooperSlider::Min() {
|
||||
return this->min;
|
||||
}
|
||||
double LooperSlider::Max() {
|
||||
return this->max;
|
||||
}
|
||||
void LooperSlider::SetLimits(double min, double max) {
|
||||
set_min(min);
|
||||
set_max(max);
|
||||
UpdateSlider(false);
|
||||
}
|
||||
void LooperSlider::SetMinLabel(const char *label) {
|
||||
if (this->min_label) free((void*)this->min_label);
|
||||
this->min_label = strdup(label);
|
||||
UpdateSlider();
|
||||
}
|
||||
void LooperSlider::SetMaxLabel(const char *label) {
|
||||
if (this->max_label) free((void*)this->max_label);
|
||||
this->max_label = strdup(label);
|
||||
UpdateSlider();
|
||||
}
|
||||
void LooperSlider::SetLimitLabels(const char *min, const char *max) {
|
||||
SetMinLabel(min);
|
||||
SetMaxLabel(max);
|
||||
}
|
||||
LooperSlider::~LooperSlider() {
|
||||
if (this->min_label) free((void*)this->min_label);
|
||||
if (this->max_label) free((void*)this->max_label);
|
||||
if (this->label) free((void*)this->label);
|
||||
}
|
||||
const char *LooperSlider::MinLabel() {
|
||||
return this->min_label;
|
||||
}
|
||||
const char *LooperSlider::MaxLabel() {
|
||||
return this->max_label;
|
||||
}
|
||||
void LooperSlider::SetLogarithmic(bool logarithmic) {
|
||||
set_logarithmic(logarithmic);
|
||||
UpdateSlider(false);
|
||||
}
|
||||
bool LooperSlider::IsLogarithmic() {
|
||||
return this->logarithmic;
|
||||
}
|
||||
void LooperSlider::SetLabel(const char *label) {
|
||||
if (this->label) free((void*)this->label);
|
||||
this->label = strdup(label);
|
||||
}
|
||||
const char *LooperSlider::Label() {
|
||||
return this->label;
|
||||
}
|
||||
double LooperSlider::Tick() {
|
||||
return this->tick;
|
||||
}
|
||||
void LooperSlider::SetTick(double value) {
|
||||
set_tick(value);
|
||||
UpdateSlider(false);
|
||||
}
|
||||
void LooperSlider::set_tick(double value) {
|
||||
this->settings_changed = true;
|
||||
this->tick = value;
|
||||
}
|
||||
void LooperSlider::UpdateSlider(bool update_value) {
|
||||
UpdateLogScaler();
|
||||
if (this->min_label == NULL) {
|
||||
this->min_label_view->setText("");
|
||||
} else {
|
||||
this->min_label_view->setText(this->min_label);
|
||||
}
|
||||
if (this->max_label == NULL) {
|
||||
this->max_label_view->setText("");
|
||||
} else {
|
||||
this->max_label_view->setText(this->max_label);
|
||||
}
|
||||
limits_visible = (this->min_label == NULL && this->max_label == NULL);
|
||||
if (limits_visible) {
|
||||
if (!limits_actually_visible) {
|
||||
this->text_layout_view->addItem(this->limits_view);
|
||||
limits_actually_visible = true;
|
||||
}
|
||||
} else {
|
||||
if (limits_actually_visible) {
|
||||
this->text_layout_view->removeItem(this->limits_view);
|
||||
limits_actually_visible = false;
|
||||
}
|
||||
}
|
||||
if (this->label == NULL) {
|
||||
if (text_label_visible) {
|
||||
this->text_layout_view->removeWidget(this->text_label);
|
||||
text_label_visible = false;
|
||||
}
|
||||
} else {
|
||||
this->text_label->setText(this->label);
|
||||
if (!text_label_visible) {
|
||||
this->text_layout_view->insertWidget(0, this->text_label);
|
||||
text_label_visible = true;
|
||||
}
|
||||
}
|
||||
if (this->text_edit_mode) {
|
||||
if (!text_editor_visible) {
|
||||
this->text_layout_view->insertWidget(1, this->text);
|
||||
text_editor_visible = true;
|
||||
}
|
||||
if (slider_visible) {
|
||||
this->text_layout_view->removeWidget(this->slider);
|
||||
slider_visible = false;
|
||||
}
|
||||
} else {
|
||||
if (!slider_visible) {
|
||||
this->text_layout_view->insertWidget(1, this->slider);
|
||||
slider_visible = true;
|
||||
}
|
||||
if (text_editor_visible) {
|
||||
this->text_layout_view->removeWidget(this->text);
|
||||
text_editor_visible = false;
|
||||
}
|
||||
}
|
||||
auto icon = QIcon::fromTheme(text_edit_mode ? "slider_edit_mode" : "text_edit_mode", QIcon::fromTheme(text_edit_mode ? "dialog-ok" : "edit"));
|
||||
auto text = text_edit_mode ? "Imprecise" : "Precise";
|
||||
if (icon.isNull() || show_button_text) {
|
||||
this->btn->setText(text);
|
||||
} else {
|
||||
this->btn->setText("");
|
||||
}
|
||||
if (show_button_icon && !icon.isNull()) {
|
||||
this->btn->setIcon(icon);
|
||||
} else {
|
||||
QIcon emptyIcon;
|
||||
this->btn->setIcon(emptyIcon);
|
||||
}
|
||||
this->slider->setVisible(slider_visible);
|
||||
this->text->setVisible(text_editor_visible);
|
||||
this->text_label->setVisible(text_label_visible);
|
||||
this->min_label_view->setVisible(limits_actually_visible);
|
||||
this->max_label_view->setVisible(limits_actually_visible);
|
||||
if (update_value) {
|
||||
if (textChanged) {
|
||||
textChanged = false;
|
||||
} else {
|
||||
this->text->setText(fmt::to_string(this->value).c_str());
|
||||
}
|
||||
if (slider_changed == 0) slider_changed = 1;
|
||||
if ((slider_changed--) == 0) {
|
||||
this->slider->setValue(this->logarithmic ? this->scaler->scale_log(this->value / this->tick) : (this->value / this->tick));
|
||||
this->slider->setMinimum(this->min / tick);
|
||||
this->slider->setMaximum(this->max / tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LooperSlider::ShowButtonText() {
|
||||
return show_button_text;
|
||||
}
|
||||
bool LooperSlider::ShowButtonIcon() {
|
||||
return show_button_icon;
|
||||
}
|
||||
void LooperSlider::SetShowButtonIcon(bool enable) {
|
||||
this->show_button_icon = enable;
|
||||
UpdateSlider();
|
||||
}
|
||||
void LooperSlider::SetShowButtonText(bool enable) {
|
||||
this->show_button_text = enable;
|
||||
UpdateSlider();
|
||||
}
|
75
backends/ui/qt/slider.hpp
Normal file
75
backends/ui/qt/slider.hpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include <QSlider>
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <util.hpp>
|
||||
class LooperSlider : public QWidget {
|
||||
Q_OBJECT;
|
||||
QSlider *slider;
|
||||
QPushButton *btn;
|
||||
QLineEdit *text;
|
||||
bool show_button_text;
|
||||
bool show_button_icon;
|
||||
QLabel *text_label;
|
||||
QLabel *min_label_view;
|
||||
QLabel *max_label_view;
|
||||
QBoxLayout *limits_view;
|
||||
QBoxLayout *root_layout;
|
||||
bool limits_visible = false;
|
||||
bool settings_changed = false;
|
||||
QBoxLayout *text_layout_view;
|
||||
bool pressed = false;
|
||||
LooperLogScaler *scaler;
|
||||
void UpdateLogScaler();
|
||||
void UpdateSlider(bool update_value = false);
|
||||
bool textChanged = false;
|
||||
bool text_editor_visible = false;
|
||||
bool slider_visible = false;
|
||||
bool text_label_visible = false;
|
||||
bool limits_actually_visible = false;
|
||||
double value;
|
||||
double tick;
|
||||
double min;
|
||||
double max;
|
||||
bool text_edit_mode = false;
|
||||
bool logarithmic;
|
||||
int slider_changed = 0;
|
||||
const char *label = NULL;
|
||||
const char *min_label = NULL;
|
||||
const char *max_label = NULL;
|
||||
void set_min(double min);
|
||||
void set_max(double max);
|
||||
void set_tick(double value);
|
||||
void set_logarithmic(bool logarithmic);
|
||||
public:
|
||||
void SetMin(double min);
|
||||
double Min();
|
||||
void SetMax(double max);
|
||||
double Max();
|
||||
void SetLimits(double min, double max);
|
||||
void SetMinLabel(const char *label);
|
||||
void SetMaxLabel(const char *label);
|
||||
const char *MinLabel();
|
||||
const char *MaxLabel();
|
||||
void SetLimitLabels(const char *min, const char *max);
|
||||
void SetValue(double value);
|
||||
double Value();
|
||||
void SetLogarithmic(bool logarithmic);
|
||||
bool IsLogarithmic();
|
||||
void SetLabel(const char *label);
|
||||
const char *Label();
|
||||
double Tick();
|
||||
bool ShowButtonText();
|
||||
bool ShowButtonIcon();
|
||||
void SetShowButtonText(bool enable);
|
||||
void SetShowButtonIcon(bool enable);
|
||||
void SetTick(double value);
|
||||
explicit LooperSlider(const char *name, const char *label, double min, double max, double tick = 0.0001, bool logarithmic = false);
|
||||
~LooperSlider();
|
||||
Q_SIGNALS:
|
||||
void changed(double value);
|
||||
};
|
4
backends/ui/qt/ui.json
Normal file
4
backends/ui/qt/ui.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"class_name": "QtUIBackend",
|
||||
"include_path": "main.h"
|
||||
}
|
43
cmake/built_sdl/SDL3Config.cmake
Normal file
43
cmake/built_sdl/SDL3Config.cmake
Normal file
|
@ -0,0 +1,43 @@
|
|||
# sdl2 cmake project-config input for CMakeLists.txt script
|
||||
|
||||
include(FeatureSummary)
|
||||
set_package_properties(SDL3 PROPERTIES
|
||||
URL "https://www.libsdl.org/"
|
||||
DESCRIPTION "low level access to audio, keyboard, mouse, joystick, and graphics hardware"
|
||||
)
|
||||
|
||||
|
||||
########################################################################
|
||||
|
||||
set(SDL3_FOUND TRUE CACHE INTERNAL "")
|
||||
|
||||
set(SDL3_SDL3_FOUND TRUE CACHE INTERNAL "")
|
||||
set(SDL3_SDL3-static_FOUND TRUE CACHE INTERNAL "")
|
||||
set(SDL3_SDL3test_FOUND OFF CACHE INTERNAL "")
|
||||
# Find SDL3::Headers
|
||||
if(NOT TARGET SDL3::Headers)
|
||||
add_library(SDL3::Headers ALIAS SDL3_Headers)
|
||||
endif()
|
||||
set(SDL3_Headers_FOUND TRUE)
|
||||
if (NOT DEFINED SDL3_FOUND)
|
||||
if (SDL2::SDL3main)
|
||||
set(SDL3_SDL3main_FOUND ON)
|
||||
else()
|
||||
set(SDL3_SDL3main_FOUND OFF)
|
||||
endif()
|
||||
set(SDL3_LIBRARY SDL3::SDL3 CACHE INTERNAL "")
|
||||
set(SDL3_INCLUDE_DIR CACHE INTERNAL "")
|
||||
set(SDL3_LIBRARIES SDL3::SDL3 CACHE INTERNAL "")
|
||||
set(SDL3_STATIC_LIBRARIES SDL3::SDL3 CACHE INTERNAL "")
|
||||
set(SDL3_STATIC_PRIVATE_LIBS "" CACHE INTERNAL "")
|
||||
set(SDL3_INCLUDE_DIRS "" CACHE INTERNAL "")
|
||||
#get_target_property(SDL2_STATIC_PRIVATE_LIBS SDL2-static LINK_LIBRARIES)
|
||||
get_target_property(_SDL3_INCLUDE_DIRS SDL3::Headers INCLUDE_DIRECTORIES)
|
||||
set(SDL3_INCLUDE_DIRS ${_SDL3_INCLUDE_DIRS} CACHE INTERNAL "")
|
||||
unset(_SDL3_INCLUDE_DIRS)
|
||||
if(SDL3_SDL3main_FOUND)
|
||||
set(SDL3MAIN_LIBRARY SDL3::SDL3main CACHE INTERNAL "")
|
||||
else()
|
||||
set(SDL3MAIN_LIBRARY CACHE INTERNAL "")
|
||||
endif()
|
||||
endif()
|
50
include/playback.h
Normal file
50
include/playback.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
#include <gio/gliststore.h>
|
||||
enum {
|
||||
/// @brief No signals have occurred.
|
||||
PlaybackSignalNone = 0,
|
||||
/// @brief The file was changed. Recheck the properties of the file because they are likely different.
|
||||
PlaybackSignalFileChanged = 1 << 0,
|
||||
/// @brief The speed was changed.
|
||||
PlaybackSignalSpeedChanged = 1 << 1,
|
||||
/// @brief The speed was changed.
|
||||
PlaybackSignalTempoChanged = 1 << 2,
|
||||
/// @brief The speed was changed.
|
||||
PlaybackSignalPitchChanged = 1 << 3,
|
||||
/// @brief Playback was paused. If @ref PlaybackSignalResumed has also been sent, you must use @ref Playback::IsPaused to check if playback was paused or resumed.
|
||||
PlaybackSignalPaused = 1 << 4,
|
||||
/// @brief Playback was resumed. If @ref PlaybackSignalPaused has also been sent, you must use @ref Playback::IsPaused to check if playback was paused or resumed.
|
||||
PlaybackSignalResumed = 1 << 5,
|
||||
/// @brief Playback was stopped entirely. If @ref PlaybackSignalStarted has also been signalled, call @ref Playback::IsStopped to find out if playback is currently playing.
|
||||
PlaybackSignalStopped = 1 << 6,
|
||||
/// @brief An error occurred and playback has likely (but not necessarily) stopped. Call @ref Playback::GetError for details.
|
||||
PlaybackSignalErrorOccurred = 1 << 7,
|
||||
/// @brief Playback was seeked by the @ref Playback::Seek function
|
||||
PlaybackSignalSeeked = 1 << 8,
|
||||
/// @brief Playback has started. If @ref PlaybackSignalStopped has also been signalled, call @ref Playback::IsStopped to find out if playback is currently playing.
|
||||
PlaybackSignalStarted = 1 << 9
|
||||
};
|
||||
G_BEGIN_DECLS
|
||||
#define LOOPER_TYPE_STREAM looper_stream_get_type()
|
||||
#define LOOPER_TYPE_PLAYBACK looper_playback_get_type()
|
||||
G_DECLARE_FINAL_TYPE(LooperStream, looper_stream, LOOPER, STREAM, GObject)
|
||||
G_DECLARE_FINAL_TYPE(LooperPlayback, looper_playback, LOOPER, PLAYBACK, GObject)
|
||||
|
||||
LooperPlayback *looper_playback_new(void);
|
||||
void looper_playback_load(LooperPlayback *self, const gchar *file_path);
|
||||
void looper_playback_start(LooperPlayback *self);
|
||||
void looper_playback_set_stream_idx(LooperPlayback *self, gint stream_idx);
|
||||
bool looper_playback_is_paused(LooperPlayback *self);
|
||||
void looper_playback_seek(LooperPlayback *self, gdouble position);
|
||||
gdouble looper_playback_get_length(LooperPlayback *self);
|
||||
gdouble looper_playback_get_position(LooperPlayback *self);
|
||||
const gchar *looper_stream_get_name(LooperStream *self);
|
||||
gdouble looper_stream_get_length(LooperStream *self);
|
||||
gint looper_stream_get_id(LooperStream *self);
|
||||
void looper_stream_set_name(LooperStream *self, const gchar *name);
|
||||
void looper_stream_set_length(LooperStream *self, gdouble length);
|
||||
void looper_stream_set_id(LooperStream *self, gint id);
|
||||
GListStore *looper_playback_get_streams(LooperPlayback *self);
|
||||
G_END_DECLS
|
329
sdl-android-project/.idea/other.xml
Normal file
329
sdl-android-project/.idea/other.xml
Normal file
|
@ -0,0 +1,329 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="direct_access_persist.xml">
|
||||
<option name="deviceSelectionList">
|
||||
<list>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="27" />
|
||||
<option name="brand" value="DOCOMO" />
|
||||
<option name="codename" value="F01L" />
|
||||
<option name="id" value="F01L" />
|
||||
<option name="manufacturer" value="FUJITSU" />
|
||||
<option name="name" value="F-01L" />
|
||||
<option name="screenDensity" value="360" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1280" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="28" />
|
||||
<option name="brand" value="DOCOMO" />
|
||||
<option name="codename" value="SH-01L" />
|
||||
<option name="id" value="SH-01L" />
|
||||
<option name="manufacturer" value="SHARP" />
|
||||
<option name="name" value="AQUOS sense2 SH-01L" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2160" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="Lenovo" />
|
||||
<option name="codename" value="TB370FU" />
|
||||
<option name="id" value="TB370FU" />
|
||||
<option name="manufacturer" value="Lenovo" />
|
||||
<option name="name" value="Tab P12" />
|
||||
<option name="screenDensity" value="340" />
|
||||
<option name="screenX" value="1840" />
|
||||
<option name="screenY" value="2944" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="31" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="a51" />
|
||||
<option name="id" value="a51" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy A51" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="akita" />
|
||||
<option name="id" value="akita" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 8a" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="b0q" />
|
||||
<option name="id" value="b0q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S22 Ultra" />
|
||||
<option name="screenDensity" value="600" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3088" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="32" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="bluejay" />
|
||||
<option name="id" value="bluejay" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 6a" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="caiman" />
|
||||
<option name="id" value="caiman" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9 Pro" />
|
||||
<option name="screenDensity" value="360" />
|
||||
<option name="screenX" value="960" />
|
||||
<option name="screenY" value="2142" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="comet" />
|
||||
<option name="id" value="comet" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9 Pro Fold" />
|
||||
<option name="screenDensity" value="390" />
|
||||
<option name="screenX" value="2076" />
|
||||
<option name="screenY" value="2152" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="29" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="crownqlteue" />
|
||||
<option name="id" value="crownqlteue" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Note9" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="2220" />
|
||||
<option name="screenY" value="1080" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="dm3q" />
|
||||
<option name="id" value="dm3q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S23 Ultra" />
|
||||
<option name="screenDensity" value="600" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3088" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="e1q" />
|
||||
<option name="id" value="e1q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S24" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="felix" />
|
||||
<option name="id" value="felix" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Fold" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="2208" />
|
||||
<option name="screenY" value="1840" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="felix" />
|
||||
<option name="id" value="felix" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Fold" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="2208" />
|
||||
<option name="screenY" value="1840" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="felix_camera" />
|
||||
<option name="id" value="felix_camera" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Fold (Camera-enabled)" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="2208" />
|
||||
<option name="screenY" value="1840" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="gts8uwifi" />
|
||||
<option name="id" value="gts8uwifi" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Tab S8 Ultra" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="1848" />
|
||||
<option name="screenY" value="2960" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="husky" />
|
||||
<option name="id" value="husky" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 8 Pro" />
|
||||
<option name="screenDensity" value="390" />
|
||||
<option name="screenX" value="1008" />
|
||||
<option name="screenY" value="2244" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="motorola" />
|
||||
<option name="codename" value="java" />
|
||||
<option name="id" value="java" />
|
||||
<option name="manufacturer" value="Motorola" />
|
||||
<option name="name" value="G20" />
|
||||
<option name="screenDensity" value="280" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="komodo" />
|
||||
<option name="id" value="komodo" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9 Pro XL" />
|
||||
<option name="screenDensity" value="360" />
|
||||
<option name="screenX" value="1008" />
|
||||
<option name="screenY" value="2244" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="lynx" />
|
||||
<option name="id" value="lynx" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 7a" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="31" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="oriole" />
|
||||
<option name="id" value="oriole" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 6" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="panther" />
|
||||
<option name="id" value="panther" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 7" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="q5q" />
|
||||
<option name="id" value="q5q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Z Fold5" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1812" />
|
||||
<option name="screenY" value="2176" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="q6q" />
|
||||
<option name="id" value="q6q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-F956B" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1856" />
|
||||
<option name="screenY" value="2160" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="r11" />
|
||||
<option name="id" value="r11" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Watch" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="384" />
|
||||
<option name="screenY" value="384" />
|
||||
<option name="type" value="WEAR_OS" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="redfin" />
|
||||
<option name="id" value="redfin" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 5" />
|
||||
<option name="screenDensity" value="440" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="shiba" />
|
||||
<option name="id" value="shiba" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 8" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="tangorpro" />
|
||||
<option name="id" value="tangorpro" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Tablet" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="1600" />
|
||||
<option name="screenY" value="2560" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="tokay" />
|
||||
<option name="id" value="tokay" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2424" />
|
||||
</PersistentDeviceSelectionData>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -1 +1 @@
|
|||
Subproject commit 720da57baba83b3b1829e20133575e57aa1a8a4f
|
||||
Subproject commit 0379bf3a5d52d8542aec1874677c9df5ff9ba5f9
|
|
@ -1 +1 @@
|
|||
Subproject commit d144031940543e15423a25ae5a8a74141044862f
|
||||
Subproject commit 6dae7eb4a5c3a169f3e298392bff4680224aa94a
|
17
test.sh
Executable file
17
test.sh
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
source venv/bin/activate
|
||||
PIDFILE="$(mktemp)"
|
||||
looper() {
|
||||
echo $$ > "$PIDFILE"
|
||||
./build/default/looper -m
|
||||
rm "$PIDFILE" -f
|
||||
}
|
||||
./build.sh || exit $?
|
||||
looper &
|
||||
tl &
|
||||
while [ -f "$PIDFILE" ]; do
|
||||
sleep 1
|
||||
done
|
||||
for i in $(jobs -p); do
|
||||
kill -HUP $i
|
||||
done
|
18
util.cpp
18
util.cpp
|
@ -245,3 +245,21 @@ PropertyHint make_hint(std::optional<double> min = {}, std::optional<double> max
|
|||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
LooperLogScaler::LooperLogScaler(double min, double max) {
|
||||
update_min_max(min, max);
|
||||
}
|
||||
void LooperLogScaler::update_min_max(double min, double max) {
|
||||
x0 = min;
|
||||
x1 = (max - min) / (exp(1.0) - 1.0);
|
||||
la = min;
|
||||
lb = (max - min);
|
||||
// x0 = scale_log(min);
|
||||
// x1 = scale_log(max);
|
||||
}
|
||||
double LooperLogScaler::scale_log(double value) {
|
||||
return (std::log(((value - x0) / x1) + 1.0) * lb) + la;
|
||||
}
|
||||
double LooperLogScaler::unscale_log(double value) {
|
||||
return ((std::exp((value - la) / lb) - 1.0) * x1) + x0;
|
||||
}
|
||||
|
|
12
util.hpp
12
util.hpp
|
@ -563,3 +563,15 @@ PropertyHint make_hint(double min, double max);
|
|||
Property make_property(PropertyType type, std::string name, PropertyId id, std::optional<PropertyHint> hint);
|
||||
Property make_property(PropertyType type, std::string name, std::string path, std::optional<PropertyHint> hint = {});
|
||||
Property make_property(PropertyType type, std::string name, PropertyId id = PropertyId::BackendSpecific, std::optional<std::string> path = {}, std::optional<PropertyHint> hint = {});
|
||||
|
||||
class LooperLogScaler {
|
||||
double la;
|
||||
double lb;
|
||||
public:
|
||||
double x0;
|
||||
double x1;
|
||||
void update_min_max(double min, double max);
|
||||
LooperLogScaler(double min, double max);
|
||||
double scale_log(double value);
|
||||
double unscale_log(double value);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue