AronaImGui/Arona/src/widget/widgets.cpp

335 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "widgets.h"
#include "audio/plugin_host/plugin_host_manager.h"
#include "audio/plugin_host/plugin_host.h"
#include "arona_application.h"
#include "audio/mixer/mixer.h"
#include "audio/mixer/mixer_track.h"
#include "imgui_internal.h"
#include "audio/device/audio_device_manager.h"
#include "imgui-knobs.h"
bool show_instrument_track = true;
bool show_mixer = true;
void draw_instrument_track() {
ImGui::Begin("Instrument", &show_instrument_track);
auto instruments = get_plugin_host_manager()->get_instrument_hosts();
for (int32_t i = 0; i < instruments.size(); ++i) {
plugin_host* host = instruments.at(i);
std::string button_label = host->name + "##" + std::to_string(i);
if (ImGui::Button(button_label.c_str())) {
host->toggle_editor();
}
}
if (ImGui::Button("Add")) {
get_arona_application()->test();
}
ImGui::End();
}
void draw_mixer() {
ImGui::Begin("Mixer", &show_mixer);
auto mixer = get_mixer();
auto mixer_tracks = mixer->get_tracks();
float delta_time = ImGui::GetIO().DeltaTime;
int32_t delta_sample_count = get_audio_device_manager()->get_sample_rate() * delta_time;
for (int32_t i = 0; i < mixer_tracks.size(); ++i) {
mixer_track* track = mixer_tracks.at(i);
draw_mixer_track(delta_sample_count, track, i);
ImGui::SameLine();
}
ImGui::End();
}
void draw_mixer_track(uint32_t delta_sample_count, mixer_track* track, int32_t index) {
float delta_time = ImGui::GetIO().DeltaTime;
ImGuiWindow* window = ImGui::GetCurrentWindow();
std::string track_label = track->get_name();
std::string child_id = "MixerTrack" + std::to_string(index);
std::string slider_id = "MixerTrackVolume##" + std::to_string(index);
std::string volume_bar_id = "VolumeBar" + std::to_string(index);
ImGui::BeginGroup();
window->DC.CurrLineTextBaseOffset = 0;
ImGui::Text(track_label.c_str());
float widget_height = ImGui::GetContentRegionAvail().y;
const ImVec2 slider_size = ImGui::CalcItemSize(ImVec2(25, widget_height), 25, widget_height);
float volume = track->get_volume();
if (vertical_volume_slider(slider_id.c_str(), slider_size, &volume, 0.f, 1.25f)) {
track->set_volume(volume);
}
ImGui::SameLine();
std::vector<sample_t> temp_buffer;
temp_buffer.reserve(delta_sample_count);
std::vector<float> sample_value;
std::vector<float> sample_peak;
for (int32_t i = 0; i < track->ui_buffers->size(); ++i) {
auto& buffer = track->ui_buffers->at(i);
auto& peak_info = track->ui_buffer_peaks.at(i);
// calculate peak
uint32_t count = std::min(delta_sample_count, buffer.Num());
temp_buffer.resize(count);
buffer.Pop(temp_buffer.data(), count);
auto peak_it = std::max_element(temp_buffer.begin(), temp_buffer.end(), [](sample_t a, sample_t b) {
return std::abs(a) < std::abs(b);
});
sample_t peak = std::abs(*peak_it);
// update peak
if (peak > peak_info.peak) {
peak_info.peak = peak;
peak_info.left_time = volume_bar_peak_duration;
} else {
peak_info.left_time -= delta_time;
if (peak_info.left_time < 0.0f) {
peak_info.peak -= delta_time * volume_bar_peak_decay;
peak_info.left_time = 0.0f;
}
}
sample_value.push_back(peak);
sample_peak.push_back(peak_info.peak);
}
draw_volume_bar(volume_bar_id.c_str(), widget_height, sample_value, sample_peak);
ImGui::SameLine();
ImGui::EndGroup();
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
get_mixer()->selected_track = track;
}
}
void draw_volume_bar(const char* id, float height, std::vector<float> sample_value, std::vector<float> sample_peak) {
#if BUILD_DEBUG
if (sample_value.size() != sample_peak.size()) {
spdlog::error("sample_value size is not equal to sample_peak size");
return;
}
#endif
ImGuiWindow* window = ImGui::GetCurrentWindow();
const ImGuiStyle& style = ImGui::GetStyle();
int32_t channel_count = sample_value.size();
float channel_width = 9.0f;
float widget_width = channel_width * channel_count;
float widget_height = height; // TODO: calculate the height
ImVec2 size_arg = ImVec2(widget_width, widget_height);
ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImGui::CalcItemSize(size_arg, widget_width, widget_height);
ImRect bb(pos, pos + size);
ImGui::ItemSize(bb);
if (!ImGui::ItemAdd(bb, ImGui::GetID(id))) {
return;
}
// 绘制背景
ImGui::RenderFrame(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
constexpr float safe_level = 0.6f; // 60% 以下绘制绿色
constexpr float warning_level = 0.8f; // 80% 以下绘制黄色
constexpr float danger_level = 1.0f; // 100% 以下绘制红色
const ImColor safe_color = ImColor(0, 255, 0, 255); // green
const ImColor warning_color = ImColor(255, 255, 0, 255); // yellow
const ImColor danger_color = ImColor(255, 0, 0, 255); // red
const ImColor bar_split_color = ImColor(0, 0, 0, 255); // white
const ImColor boarder_color = ImColor(0, 140, 120, 255); // black
window->DrawList->PushClipRect(bb.Min, bb.Max, true);
ImVec2 temp_pos = pos;
// 坐标系原点在左上角
for (int32_t i = 0; i < channel_count; ++i) {
float value_db = gain_to_db(sample_value.at(i));
float value = db_to_percentage_log(value_db, -75.0f, 6.0f);
float peak_db = gain_to_db(sample_peak.at(i));
float peak = db_to_percentage_log(peak_db, -75.0f, 6.0f);
// 绘制矩形(60%以下的部分绘制绿色80%以下的部分绘制黄色100%以下的部分绘制红色)
if (value > volume_bar_inf) {
ImVec2 clip_bar_start = ImVec2(temp_pos.x, temp_pos.y + widget_height * (1.0f - value));
ImVec2 clip_bar_end = ImVec2(temp_pos.x + channel_width, temp_pos.y + widget_height);
ImRect bar_rect(clip_bar_start, clip_bar_end);
window->DrawList->PushClipRect(bar_rect.Min, bar_rect.Max, true);
// 绘制100%的部分
{
float bar_height = widget_height * danger_level;
ImVec2 bar_start = ImVec2(temp_pos.x, temp_pos.y + widget_height - bar_height);
ImVec2 bar_end = ImVec2(temp_pos.x + channel_width, temp_pos.y + widget_height);
window->DrawList->AddRectFilled(bar_start, bar_end, danger_color);
}
// 绘制80%的部分
{
float bar_height = widget_height * warning_level;
ImVec2 bar_start = ImVec2(temp_pos.x, temp_pos.y + widget_height - bar_height);
ImVec2 bar_end = ImVec2(temp_pos.x + channel_width, temp_pos.y + widget_height);
window->DrawList->AddRectFilled(bar_start, bar_end, warning_color);
}
// 绘制60%以下的部分
{
float bar_height = widget_height * safe_level;
ImVec2 bar_start = ImVec2(temp_pos.x, temp_pos.y + widget_height - bar_height);
ImVec2 bar_end = ImVec2(temp_pos.x + channel_width, temp_pos.y + widget_height);
window->DrawList->AddRectFilled(bar_start, bar_end, safe_color);
}
window->DrawList->PopClipRect();
}
// 绘制Peak
if (peak > volume_bar_inf) {
const ImColor peak_color = peak <= safe_level ? safe_color : peak <= warning_level ? warning_color : danger_color;
float peak_height = temp_pos.y + widget_height * (1 - peak);
ImVec2 peak_start = ImVec2(temp_pos.x, peak_height);
ImVec2 peak_end = ImVec2(temp_pos.x + channel_width, peak_height + 1);
window->DrawList->AddRectFilled(peak_start, peak_end, peak_color);
}
temp_pos.x += channel_width;
}
temp_pos = pos;
// 绘制垂直分割线
for (int32_t i = 0; i < channel_count; ++i) {
if (i != channel_count - 1) {
ImVec2 line_start = ImVec2(temp_pos.x + channel_width - 0.5f, temp_pos.y);
ImVec2 line_end = ImVec2(temp_pos.x + channel_width - 0.5f, temp_pos.y + widget_height);
window->DrawList->AddLine(line_start, line_end, bar_split_color);
}
}
// 绘制边框
window->DrawList->AddRect(bb.Min, bb.Max, boarder_color);
window->DrawList->PopClipRect();
}
bool vertical_volume_slider(const char* id, ImVec2 size, float* volume, float min_volume, float max_volume) {
ImGuiWindow *window = ImGui::GetCurrentWindow();
ImGuiIO &io = ImGui::GetIO();
ImVec2 cur_pos = window->DC.CursorPos;
ImRect bb(cur_pos, cur_pos + size);
ImGui::ItemSize(size, 0.0f);
if (!ImGui::ItemAdd(bb, 0))
return false;
ImDrawList *draw_list = ImGui::GetWindowDrawList();
ImGui::RenderFrame(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, 0.0f);
float percent = mapping(*volume, min_volume, max_volume, 0.f, 1.0f);
ImVec2 pos = ImVec2(bb.Min.x, bb.Min.y + size.y * (1 - percent));
draw_list->AddRectFilled(pos, bb.Max, ImGui::GetColorU32(ImGuiCol_SliderGrabActive), 0.0f);
bool on_hovered = ImGui::IsItemHovered();
if (on_hovered) {
ImGui::BeginTooltip();
ImGui::Text("Volume: %.3fdb", 20.0f * log10f(*volume));
ImGui::EndTooltip();
}
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
*volume = 1.0f;
return true;
}
static float drag_start_value = 0.0f;
static ImGuiID dragging = 0;
const bool clicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
const bool released = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
ImGuiID current_id = ImGui::GetID(id);
if (released) {
dragging = 0;
} else if (!dragging && on_hovered && clicked) {
drag_start_value = *volume;
dragging = current_id;
}
if (dragging != current_id) {
return false;
}
float mouse_drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left).y;
float delta = (mouse_drag_delta / size.y) * (max_volume - min_volume);
*volume = std::clamp(drag_start_value - delta, min_volume, max_volume);
return true;
}
void draw_mixer_track_effect_list(mixer_track* track) {
ImGui::Begin("TrackEffects");
if (track) {
for (auto effect: track->effects) {
draw_mixer_track_effect(effect);
ImGui::SameLine();
}
if (ImGui::Button("Add")) {
auto effect = get_plugin_host_manager()->create_effect_plugin_host(R"(F:\VST\VST64\OTT_x64.dll)");
track->add_effect(effect);
}
} else {
ImGui::Text("No track selected");
}
ImGui::End();
}
void draw_selected_mixer_track_effect_list() {
mixer_track* track = get_mixer()->selected_track;
draw_mixer_track_effect_list(track);
}
void draw_mixer_track_effect(plugin_host* effect) {
auto effect_id = effect->get_imgui_id();
ImGui::BeginGroup();
if (ImGui::Button(effect->name.c_str())) {
effect->toggle_editor();
}
auto param_count = effect->get_param_count();
for (int i = 0; i < param_count; ++i) {
host_param param_info(effect, i);
const auto& param_name = param_info.get_name();
auto label = param_info.get_label();
if (label == "%") {
label += "%";
}
const auto& param_display = param_info.get_display() + label;
auto slider_id = param_name + "##" + std::to_string(i);
bool is_toggle = param_info.is_toggle();
if (is_toggle) {
bool param_value = param_info.get_value() > 0.5f;
if (ImGui::Checkbox(slider_id.c_str(), &param_value)) {
param_info.set_value(param_value);
}
} else {
float param_value = param_info.get_value();
if (ImGuiKnobs::Knob(param_name.c_str(), &param_value, param_info.min_value(), param_info.max_value(), 0,
param_display.c_str())) {
param_info.set_value(param_value);
}
}
// if (i % 2 == 0)
// ImGui::SameLine();
// else
// ImGui::NewLine();
}
ImGui::EndGroup();
// right click open menu
if (ImGui::BeginPopupContextItem(effect_id.c_str())) {
if (ImGui::MenuItem("Remove")) {
delete_effect(get_mixer()->selected_track, effect);
}
ImGui::EndPopup();
}
}