#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 temp_buffer; temp_buffer.reserve(delta_sample_count); std::vector sample_value; std::vector 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 sample_value, std::vector 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(), ¶m_value)) { param_info.set_value(param_value); } } else { float param_value = param_info.get_value(); if (ImGuiKnobs::Knob(param_name.c_str(), ¶m_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(); } }