新增imgui-knobs, 新增释放管理器顺序依赖, 修复plugin_host窗口没有打开的问题

This commit is contained in:
Nanako 2024-03-01 14:48:24 +08:00
parent cb3b2e0c6a
commit c957b60ec8
16 changed files with 423 additions and 24 deletions

View File

@ -13,8 +13,8 @@ void window_manager::init(singleton_initliazer& initliazer) {
// start_idle_thread();
}
void window_manager::release() {
singleton_t<window_manager>::release();
void window_manager::release(singleton_release_guard& release_guard) {
singleton_t<window_manager>::release(release_guard);
if (main_window_)
glfwDestroyWindow(main_window_);
}
@ -53,6 +53,7 @@ GLFWwindow* window_manager::create_plugin_host_window(plugin_host* host) {
glfwSetWindowPos(new_window, pos.x, pos.y);
}
host_window_map_[host] = new_window;
glfwShowWindow(new_window);
return new_window;
}

View File

@ -11,7 +11,7 @@ class window_manager : public singleton_t<window_manager> {
public:
window_manager();
void init(singleton_initliazer& initliazer) override;
void release() override;
void release(singleton_release_guard& release_guard) override;
void tick();
bool should_close() const { return glfwWindowShouldClose(main_window_); }
void destroy_all_plugin_host_window();

View File

@ -14,8 +14,9 @@ void audio_device_manager::init(singleton_initliazer& initliazer) {
dummy_audio_device_->init();
}
void audio_device_manager::release() {
singleton_t<audio_device_manager>::release();
void audio_device_manager::release(singleton_release_guard& release_guard) {
singleton_t<audio_device_manager>::release(release_guard);
release_guard.require_release<mixer>();
delete dummy_audio_device_;
delete main_audio_device_;
}

View File

@ -7,7 +7,7 @@ class port_audio_device;
class audio_device_manager : public singleton_t<audio_device_manager> {
public:
void init(singleton_initliazer& initliazer) override;
void release() override;
void release(singleton_release_guard& release_guard) override;
void start();

View File

@ -14,6 +14,7 @@ int port_audio_callback(const void* Input, void* Output, unsigned long FrameCoun
port_audio_device::~port_audio_device() {
port_audio_device::destroy();
stop();
}
bool port_audio_device::init() {

View File

@ -77,8 +77,8 @@ void mixer::init(singleton_initliazer& initliazer) {
device_manager->start();
}
void mixer::release() {
singleton_t<mixer>::release();
void mixer::release(singleton_release_guard& release_guard) {
singleton_t<mixer>::release(release_guard);
null_channel_node::destroy();
for (const mixer_track* track : tracks_) {

View File

@ -15,7 +15,7 @@ class mixer : public singleton_t<mixer> {
public:
void init(singleton_initliazer& initliazer) override;
void release() override;
void release(singleton_release_guard& release_guard) override;
const char* get_name() override { return "mixer"; }
dummy_track* create_dummy_track(const std::string& in_name);

View File

@ -17,9 +17,11 @@ void plugin_host_manager::init(singleton_initliazer& initliazer) {
mixer_ptr->on_remove_track.add_raw(this, &plugin_host_manager::on_mixer_track_removed);
}
void plugin_host_manager::release() {
singleton_t<plugin_host_manager>::release();
for (auto host: plugin_hosts_) {
void plugin_host_manager::release(singleton_release_guard& release_guard) {
singleton_t<plugin_host_manager>::release(release_guard);
release_guard.require_release<audio_device_manager>();
for (const plugin_host* host: plugin_hosts_) {
delete host;
}
}
@ -63,6 +65,7 @@ plugin_host* plugin_host_manager::load_plugin(const char* path) {
}
void plugin_host_manager::register_instrument_plugin(plugin_host* host) {
instrument_plugins_.push_back(host);
g_audio_thread_hub.push_message([this, host]() {
instrument_track* instrument_track = g_mixer.create_instrument_track(host);
host->channel->set_input_channel(instrument_track->get_channel_interface()->input_channel_nodes);

View File

@ -8,7 +8,7 @@ class mixer_track;
class CORE_API plugin_host_manager : public singleton_t<plugin_host_manager> {
public:
void init(singleton_initliazer& initliazer) override;
void release() override;
void release(singleton_release_guard& release_guard) override;
plugin_host* create_instrument_plugin_host(const char* path);

View File

@ -28,8 +28,8 @@ public:
virtual ~singleton() = default;
virtual void init(singleton_initliazer& initliazer) {}
virtual void post_init() {}
virtual void begin_release() {}
virtual void release() {}
virtual void begin_release(singleton_release_guard& release_guard) {}
virtual void release(singleton_release_guard& release_guard) {}
virtual const char* get_name() = 0;
};

View File

@ -13,6 +13,20 @@ void singleton_initliazer::init_singleton(singleton* s) {
}
}
bool singleton_release_guard::has_release(singleton* s) {
return std::ranges::find(release_singletons_, s) != release_singletons_.end();
}
void singleton_release_guard::release_singleton(singleton* s) {
if (!has_release(s)) {
release_singletons_.push_back(s);
if (begin_release_)
s->begin_release(*this);
else
s->release(*this);
}
}
void singleton_manager::add(singleton* s) {
singletons_.push_back(s);
}
@ -29,12 +43,18 @@ void singleton_manager::init() {
}
void singleton_manager::release() const {
for (int32_t j = singletons_.size() - 1; j >= 0; --j) {
auto s = singletons_[j];
s->begin_release();
{
singleton_release_guard release_guard(true);
for (int32_t j = singletons_.size() - 1; j >= 0; --j) {
auto s = singletons_[j];
release_guard.release_singleton(s);
}
}
for (int32_t j = singletons_.size() - 1; j >= 0; --j) {
auto s = singletons_[j];
s->release();
{
singleton_release_guard release_guard(false);
for (int32_t j = singletons_.size() - 1; j >= 0; --j) {
auto s = singletons_[j];
release_guard.release_singleton(s);
}
}
}

View File

@ -14,14 +14,36 @@ public:
if (has_init(instance)) {
return instance;
}
singletons_.push_back(instance);
instance->init(*this);
init_singleton(instance);
return instance;
}
std::vector<singleton*> singletons_{};
};
class singleton_release_guard {
public:
singleton_release_guard(bool begin_release)
: begin_release_(begin_release) {
}
bool has_release(singleton* s);
void release_singleton(singleton* s);
template<class T>
void require_release() {
auto instance = T::get();
if (has_release(instance)) {
return;
}
release_singleton(instance);
}
std::vector<singleton*> release_singletons_{};
private:
bool begin_release_;
};
class CORE_API singleton_manager {
public:
static singleton_manager* get() {

View File

@ -15,6 +15,8 @@ add_library(${PROJECT_NAME} STATIC
imgui/imstb_textedit.h
imgui/imstb_truetype.h
imgui/imgui_tables.cpp
imgui-knobs/imgui-knobs.h
imgui-knobs/imgui-knobs.cpp
)
find_package(Vulkan REQUIRED)
@ -29,6 +31,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC glfw ${Vulkan_LIBRARIES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/imgui)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/imgui/backends)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/imgui-knobs)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} glfw ${Vulkan_INCLUDE_DIRS})
target_precompile_headers(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/pch.h)

@ -1 +1 @@
Subproject commit 8048b52498a9bf2a9f87b080d43b0bfd7a5d51d8
Subproject commit 77dff5a735afd821fea1ac54dda63ef5d8bded96

View File

@ -0,0 +1,303 @@
#include "imgui-knobs.h"
#include <cmath>
#include <cstdlib>
#include <imgui.h>
#include <imgui_internal.h>
#define IMGUIKNOBS_PI 3.14159265358979323846f
namespace ImGuiKnobs {
namespace detail {
void draw_arc1(ImVec2 center, float radius, float start_angle, float end_angle, float thickness, ImColor color, int num_segments) {
ImVec2 start = {
center[0] + cosf(start_angle) * radius,
center[1] + sinf(start_angle) * radius,
};
ImVec2 end = {
center[0] + cosf(end_angle) * radius,
center[1] + sinf(end_angle) * radius,
};
// Calculate bezier arc points
auto ax = start[0] - center[0];
auto ay = start[1] - center[1];
auto bx = end[0] - center[0];
auto by = end[1] - center[1];
auto q1 = ax * ax + ay * ay;
auto q2 = q1 + ax * bx + ay * by;
auto k2 = (4.0f / 3.0f) * (sqrtf((2.0f * q1 * q2)) - q2) / (ax * by - ay * bx);
auto arc1 = ImVec2{center[0] + ax - k2 * ay, center[1] + ay + k2 * ax};
auto arc2 = ImVec2{center[0] + bx + k2 * by, center[1] + by - k2 * bx};
auto *draw_list = ImGui::GetWindowDrawList();
#if IMGUI_VERSION_NUM <= 18000
draw_list->AddBezierCurve(start, arc1, arc2, end, color, thickness, num_segments);
#else
draw_list->AddBezierCubic(start, arc1, arc2, end, color, thickness, num_segments);
#endif
}
void draw_arc(ImVec2 center, float radius, float start_angle, float end_angle, float thickness, ImColor color, int num_segments, int bezier_count) {
// Overlap and angle of ends of bezier curves needs work, only looks good when not transperant
auto overlap = thickness * radius * 0.00001f * IMGUIKNOBS_PI;
auto delta = end_angle - start_angle;
auto bez_step = 1.0f / bezier_count;
auto mid_angle = start_angle + overlap;
for (auto i = 0; i < bezier_count - 1; i++) {
auto mid_angle2 = delta * bez_step + mid_angle;
draw_arc1(center, radius, mid_angle - overlap, mid_angle2 + overlap, thickness, color, num_segments);
mid_angle = mid_angle2;
}
draw_arc1(center, radius, mid_angle - overlap, end_angle, thickness, color, num_segments);
}
template<typename DataType>
struct knob {
float radius;
bool value_changed;
ImVec2 center;
bool is_active;
bool is_hovered;
float angle_min;
float angle_max;
float t;
float angle;
float angle_cos;
float angle_sin;
knob(const char *_label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, float _radius, const char *format, ImGuiKnobFlags flags) {
radius = _radius;
t = ((float) *p_value - v_min) / (v_max - v_min);
auto screen_pos = ImGui::GetCursorScreenPos();
// Handle dragging
ImGui::InvisibleButton(_label, {radius * 2.0f, radius * 2.0f});
auto gid = ImGui::GetID(_label);
ImGuiSliderFlags drag_flags = 0;
if (!(flags & ImGuiKnobFlags_DragHorizontal)) {
drag_flags |= ImGuiSliderFlags_Vertical;
}
value_changed = ImGui::DragBehavior(gid, data_type, p_value, speed, &v_min, &v_max, format, drag_flags);
angle_min = IMGUIKNOBS_PI * 0.75f;
angle_max = IMGUIKNOBS_PI * 2.25f;
center = {screen_pos[0] + radius, screen_pos[1] + radius};
is_active = ImGui::IsItemActive();
is_hovered = ImGui::IsItemHovered();
angle = angle_min + (angle_max - angle_min) * t;
angle_cos = cosf(angle);
angle_sin = sinf(angle);
}
void draw_dot(float size, float radius, float angle, color_set color, bool filled, int segments) {
auto dot_size = size * this->radius;
auto dot_radius = radius * this->radius;
ImGui::GetWindowDrawList()->AddCircleFilled(
{center[0] + cosf(angle) * dot_radius, center[1] + sinf(angle) * dot_radius},
dot_size,
is_active ? color.active : (is_hovered ? color.hovered : color.base),
segments);
}
void draw_tick(float start, float end, float width, float angle, color_set color) {
auto tick_start = start * radius;
auto tick_end = end * radius;
auto angle_cos = cosf(angle);
auto angle_sin = sinf(angle);
ImGui::GetWindowDrawList()->AddLine(
{center[0] + angle_cos * tick_end, center[1] + angle_sin * tick_end},
{center[0] + angle_cos * tick_start, center[1] + angle_sin * tick_start},
is_active ? color.active : (is_hovered ? color.hovered : color.base),
width * radius);
}
void draw_circle(float size, color_set color, bool filled, int segments) {
auto circle_radius = size * radius;
ImGui::GetWindowDrawList()->AddCircleFilled(
center,
circle_radius,
is_active ? color.active : (is_hovered ? color.hovered : color.base));
}
void draw_arc(float radius, float size, float start_angle, float end_angle, color_set color, int segments, int bezier_count) {
auto track_radius = radius * this->radius;
auto track_size = size * this->radius * 0.5f + 0.0001f;
detail::draw_arc(
center,
track_radius,
start_angle,
end_angle,
track_size,
is_active ? color.active : (is_hovered ? color.hovered : color.base),
segments,
bezier_count);
}
};
template<typename DataType>
knob<DataType> knob_with_drag(const char *label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float _speed, const char *format, float size, ImGuiKnobFlags flags) {
auto speed = _speed == 0 ? (v_max - v_min) / 250.f : _speed;
ImGui::PushID(label);
auto width = size == 0 ? ImGui::GetTextLineHeight() * 4.0f : size * ImGui::GetIO().FontGlobalScale;
ImGui::PushItemWidth(width);
ImGui::BeginGroup();
// There's an issue with `SameLine` and Groups, see https://github.com/ocornut/imgui/issues/4190.
// This is probably not the best solution, but seems to work for now
ImGui::GetCurrentWindow()->DC.CurrLineTextBaseOffset = 0;
// Draw title
if (!(flags & ImGuiKnobFlags_NoTitle)) {
auto title_size = ImGui::CalcTextSize(label, NULL, false, width);
// Center title
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (width - title_size[0]) * 0.5f);
ImGui::Text("%s", label);
}
// Draw knob
knob<DataType> k(label, data_type, p_value, v_min, v_max, speed, width * 0.5f, format, flags);
// Draw tooltip
if (flags & ImGuiKnobFlags_ValueTooltip && (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) || ImGui::IsItemActive())) {
ImGui::BeginTooltip();
ImGui::Text(format, *p_value);
ImGui::EndTooltip();
}
// Draw input
if (!(flags & ImGuiKnobFlags_NoInput)) {
ImGuiSliderFlags drag_flags = 0;
if (!(flags & ImGuiKnobFlags_DragHorizontal)) {
drag_flags |= ImGuiSliderFlags_Vertical;
}
auto changed = ImGui::DragScalar("###knob_drag", data_type, p_value, speed, &v_min, &v_max, format, drag_flags);
if (changed) {
k.value_changed = true;
}
}
ImGui::EndGroup();
ImGui::PopItemWidth();
ImGui::PopID();
return k;
}
color_set GetPrimaryColorSet() {
auto *colors = ImGui::GetStyle().Colors;
return {colors[ImGuiCol_ButtonActive], colors[ImGuiCol_ButtonHovered], colors[ImGuiCol_ButtonHovered]};
}
color_set GetSecondaryColorSet() {
auto *colors = ImGui::GetStyle().Colors;
auto active = ImVec4(
colors[ImGuiCol_ButtonActive].x * 0.5f,
colors[ImGuiCol_ButtonActive].y * 0.5f,
colors[ImGuiCol_ButtonActive].z * 0.5f,
colors[ImGuiCol_ButtonActive].w);
auto hovered = ImVec4(
colors[ImGuiCol_ButtonHovered].x * 0.5f,
colors[ImGuiCol_ButtonHovered].y * 0.5f,
colors[ImGuiCol_ButtonHovered].z * 0.5f,
colors[ImGuiCol_ButtonHovered].w);
return {active, hovered, hovered};
}
color_set GetTrackColorSet() {
auto *colors = ImGui::GetStyle().Colors;
return {colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBg]};
}
}// namespace detail
template<typename DataType>
bool BaseKnob(const char *label, ImGuiDataType data_type, DataType *p_value, DataType v_min, DataType v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps = 10) {
auto knob = detail::knob_with_drag(label, data_type, p_value, v_min, v_max, speed, format, size, flags);
switch (variant) {
case ImGuiKnobVariant_Tick: {
knob.draw_circle(0.85f, detail::GetSecondaryColorSet(), true, 32);
knob.draw_tick(0.5f, 0.85f, 0.08f, knob.angle, detail::GetPrimaryColorSet());
break;
}
case ImGuiKnobVariant_Dot: {
knob.draw_circle(0.85f, detail::GetSecondaryColorSet(), true, 32);
knob.draw_dot(0.12f, 0.6f, knob.angle, detail::GetPrimaryColorSet(), true, 12);
break;
}
case ImGuiKnobVariant_Wiper: {
knob.draw_circle(0.7f, detail::GetSecondaryColorSet(), true, 32);
knob.draw_arc(0.8f, 0.41f, knob.angle_min, knob.angle_max, detail::GetTrackColorSet(), 16, 2);
if (knob.t > 0.01f) {
knob.draw_arc(0.8f, 0.43f, knob.angle_min, knob.angle, detail::GetPrimaryColorSet(), 16, 2);
}
break;
}
case ImGuiKnobVariant_WiperOnly: {
knob.draw_arc(0.8f, 0.41f, knob.angle_min, knob.angle_max, detail::GetTrackColorSet(), 32, 2);
if (knob.t > 0.01) {
knob.draw_arc(0.8f, 0.43f, knob.angle_min, knob.angle, detail::GetPrimaryColorSet(), 16, 2);
}
break;
}
case ImGuiKnobVariant_WiperDot: {
knob.draw_circle(0.6f, detail::GetSecondaryColorSet(), true, 32);
knob.draw_arc(0.85f, 0.41f, knob.angle_min, knob.angle_max, detail::GetTrackColorSet(), 16, 2);
knob.draw_dot(0.1f, 0.85f, knob.angle, detail::GetPrimaryColorSet(), true, 12);
break;
}
case ImGuiKnobVariant_Stepped: {
for (auto n = 0.f; n < steps; n++) {
auto a = n / (steps - 1);
auto angle = knob.angle_min + (knob.angle_max - knob.angle_min) * a;
knob.draw_tick(0.7f, 0.9f, 0.04f, angle, detail::GetPrimaryColorSet());
}
knob.draw_circle(0.6f, detail::GetSecondaryColorSet(), true, 32);
knob.draw_dot(0.12f, 0.4f, knob.angle, detail::GetPrimaryColorSet(), true, 12);
break;
}
case ImGuiKnobVariant_Space: {
knob.draw_circle(0.3f - knob.t * 0.1f, detail::GetSecondaryColorSet(), true, 16);
if (knob.t > 0.01f) {
knob.draw_arc(0.4f, 0.15f, knob.angle_min - 1.0f, knob.angle - 1.0f, detail::GetPrimaryColorSet(), 16, 2);
knob.draw_arc(0.6f, 0.15f, knob.angle_min + 1.0f, knob.angle + 1.0f, detail::GetPrimaryColorSet(), 16, 2);
knob.draw_arc(0.8f, 0.15f, knob.angle_min + 3.0f, knob.angle + 3.0f, detail::GetPrimaryColorSet(), 16, 2);
}
break;
}
}
return knob.value_changed;
}
bool Knob(const char *label, float *p_value, float v_min, float v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps) {
const char *_format = format == NULL ? "%.3f" : format;
return BaseKnob(label, ImGuiDataType_Float, p_value, v_min, v_max, speed, _format, variant, size, flags, steps);
}
bool KnobInt(const char *label, int *p_value, int v_min, int v_max, float speed, const char *format, ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags, int steps) {
const char *_format = format == NULL ? "%i" : format;
return BaseKnob(label, ImGuiDataType_S32, p_value, v_min, v_max, speed, _format, variant, size, flags, steps);
}
}// namespace ImGuiKnobs

View File

@ -0,0 +1,45 @@
#pragma once
#include <cstdlib>
#include <imgui.h>
typedef int ImGuiKnobFlags;
enum ImGuiKnobFlags_ {
ImGuiKnobFlags_NoTitle = 1 << 0,
ImGuiKnobFlags_NoInput = 1 << 1,
ImGuiKnobFlags_ValueTooltip = 1 << 2,
ImGuiKnobFlags_DragHorizontal = 1 << 3,
};
typedef int ImGuiKnobVariant;
enum ImGuiKnobVariant_ {
ImGuiKnobVariant_Tick = 1 << 0,
ImGuiKnobVariant_Dot = 1 << 1,
ImGuiKnobVariant_Wiper = 1 << 2,
ImGuiKnobVariant_WiperOnly = 1 << 3,
ImGuiKnobVariant_WiperDot = 1 << 4,
ImGuiKnobVariant_Stepped = 1 << 5,
ImGuiKnobVariant_Space = 1 << 6,
};
namespace ImGuiKnobs {
struct color_set {
ImColor base;
ImColor hovered;
ImColor active;
color_set(ImColor base, ImColor hovered, ImColor active) : base(base), hovered(hovered), active(active) {}
color_set(ImColor color) {
base = color;
hovered = color;
active = color;
}
};
bool Knob(const char *label, float *p_value, float v_min, float v_max, float speed = 0, const char *format = NULL, ImGuiKnobVariant variant = ImGuiKnobVariant_Tick, float size = 0, ImGuiKnobFlags flags = 0, int steps = 10);
bool KnobInt(const char *label, int *p_value, int v_min, int v_max, float speed = 0, const char *format = NULL, ImGuiKnobVariant variant = ImGuiKnobVariant_Tick, float size = 0, ImGuiKnobFlags flags = 0, int steps = 10);
}// namespace ImGuiKnobs