Files
AronaImGui/Arona/src/widget/widgets.cpp
2024-06-13 21:15:10 +08:00

244 lines
9.1 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 "imgui.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"
bool show_instrument_track = true;
bool show_mixer = true;
void vertical_text(const char* text) {
if (!text)
return;
ImGuiWindow* window = ImGui::GetCurrentWindow();
ImGuiIO& io = ImGui::GetIO();
const ImFont* font = io.Fonts->Fonts[0];
float font_size = ImGui::GetFontSize();
ImVec2 cur_pos = window->DC.CursorPos;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 text_size = ImGui::CalcTextSize(text, nullptr, true);
ImRect bb(cur_pos, cur_pos + text_size);
for (const char* p = text; *p; p++)
{
const ImFontGlyph* glyph = font->FindGlyph(*p);
if (!glyph)
continue;
draw_list->PrimReserve(6, 4);
draw_list->PrimRectUV(
cur_pos,
ImVec2(cur_pos.x + font_size, cur_pos.y + (glyph->Y1 - glyph->Y0) * io.FontGlobalScale),
ImVec2(glyph->U0, glyph->V0),
ImVec2(glyph->U1, glyph->V1),
ImGui::GetColorU32(ImGuiCol_Text)
);
cur_pos.y += (glyph->AdvanceX * io.FontGlobalScale);
}
ImGui::ItemSize(text_size, 0.0f);
ImGui::ItemAdd(bb, 0);
}
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 update_mixer_track_peak(float delta_time, mixer_track* track) {
}
void draw_mixer_track(uint32_t delta_sample_count, mixer_track* track, int32_t index) {
float delta_time = ImGui::GetIO().DeltaTime;
std::string track_label = track->get_name();
std::string child_id = "MixerTrack" + std::to_string(index);
std::string slider_id = "##" + std::to_string(index);
std::string volume_bar_id = "VolumeBar" + std::to_string(index);
ImGui::BeginGroup();
ImGui::Text(track_label.c_str());
float widget_height = ImGui::GetContentRegionAvail().y;
const ImVec2 slider_size = ImGui::CalcItemSize(ImVec2(20, widget_height), 20, widget_height);
float volume = track->get_volume();
if (ImGui::VSliderFloat(slider_id.c_str(), slider_size,&volume, 0.0f, 1.2f)) {
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);
float peak_db = sample_to_db(peak);
float peak_db_percent = db_to_percent(peak_db);
// update peak
if (peak_db_percent > peak_info.peak) {
peak_info.peak = peak_db_percent;
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_db_percent);
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();
// on hover
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text(track_label.c_str());
ImGui::EndTooltip();
}
}
void draw_volume_bar(const char* id, float height, std::vector<float> sample_value, std::vector<float> sample_peak) {
// draw a 2d box
// calculate the size
#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 = 6.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
window->DrawList->PushClipRect(bb.Min, bb.Max, true);
// 坐标系原点在左上角
for (int32_t i = 0; i < channel_count; ++i) {
float value = sample_value.at(i);
float peak = sample_peak.at(i);
// 绘制矩形(60%以下的部分绘制绿色80%以下的部分绘制黄色100%以下的部分绘制红色)
if (value > volume_bar_inf) {
ImVec2 clip_bar_start = ImVec2(pos.x, pos.y + widget_height * (1.0f - value));
ImVec2 clip_bar_end = ImVec2(pos.x + channel_width, 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(pos.x, pos.y + widget_height - bar_height);
ImVec2 bar_end = ImVec2(pos.x + channel_width, 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(pos.x, pos.y + widget_height - bar_height);
ImVec2 bar_end = ImVec2(pos.x + channel_width, 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(pos.x, pos.y + widget_height - bar_height);
ImVec2 bar_end = ImVec2(pos.x + channel_width, 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 = pos.y + widget_height * (1 - peak);
ImVec2 peak_start = ImVec2(pos.x, peak_height);
ImVec2 peak_end = ImVec2(pos.x + channel_width, peak_height + 1);
window->DrawList->AddRectFilled(peak_start, peak_end, peak_color);
}
// 绘制垂直分割线
if (i != channel_count - 1) {
ImVec2 line_start = ImVec2(pos.x + channel_width, pos.y);
ImVec2 line_end = ImVec2(pos.x + channel_width, pos.y + widget_height);
window->DrawList->AddLine(line_start, line_end, ImGui::GetColorU32(ImGuiCol_Header));
}
pos.x += channel_width;
}
window->DrawList->PopClipRect();
}