diff --git a/Arona/widget/arona_widget.cpp b/Arona/widget/arona_widget.cpp index f06e19a..e065693 100644 --- a/Arona/widget/arona_widget.cpp +++ b/Arona/widget/arona_widget.cpp @@ -4,7 +4,7 @@ using namespace ImGui; -void draw_volume_bar(float volume, float peak, const ImVec2& size_arg, const char* label) { +void draw_volume_bar(float volume_dB, float peak_dB, const ImVec2& size_arg, const char* label) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; @@ -21,19 +21,24 @@ void draw_volume_bar(float volume, float peak, const ImVec2& size_arg, const cha window->DrawList->PushClipRect(bb.Min, bb.Max, true); // Render - volume = ImSaturate(volume); + // volume_dB = ImSaturate(volume_dB); RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); - const float y = bb.Max.y - ImLerp(0.0f, size.y, volume); + const float y_indicator = map_db_to_non_linear_volume_indicator(volume_dB, -90.0f, 12.0f, 0.0f, size.y); + + // const float y = bb.Max.y - ImLerp(0.0f, size.y, volume_dB); + const float y = bb.Max.y - y_indicator; const ImVec2 fill_min = ImVec2(bb.Min.x, y); const ImVec2 fill_br = ImVec2(bb.Max.x, bb.Max.y); window->DrawList->AddRectFilled(fill_min, fill_br, GetColorU32(ImGuiCol_PlotHistogram)); // Peak - if (peak > 0.0f) { - const float peak_y = bb.Max.y - ImLerp(0.0f, size.y, peak); + if (peak_dB > -90.f) { + const float peak_y_indicator = map_db_to_non_linear_volume_indicator(peak_dB, -90.0f, 12.0f, 0.0f, size.y); + // const float peak_y = bb.Max.y - ImLerp(0.0f, size.y, peak_dB); + const float peak_y = bb.Max.y - peak_y_indicator; if (peak_y < fill_min.y + 1.0f) // Draw line at the top of the fill window->DrawList->AddLine(ImVec2(bb.Min.x, peak_y), ImVec2(bb.Max.x, peak_y), GetColorU32(ImGuiCol_Text)); } diff --git a/Arona/widget/arona_widget.h b/Arona/widget/arona_widget.h index 4048dac..f4a7721 100644 --- a/Arona/widget/arona_widget.h +++ b/Arona/widget/arona_widget.h @@ -2,4 +2,55 @@ #include "imgui.h" -void draw_volume_bar(float volume, float peak, const ImVec2& size_arg, const char* label); +void draw_volume_bar(float volume_dB, float peak_dB, const ImVec2& size_arg, const char* label); + +// 计算RMS值 +inline float calculate_rms(const std::vector& samples) { + float sum = 0.0; + for (const float sample : samples) { + sum += sample * sample; + } + return std::sqrt(sum / samples.size()); +} + +// 将RMS值转换为dB +inline float rms_2_db(float rms, float rms_ref) { + return 20.0 * std::log10(rms / rms_ref); +} + +template +static constexpr inline auto get_range_pct(T min_value, T max_value, T2 in_value) +{ + // Avoid Divide by Zero. + // But also if our range is a point, output whether Value is before or after. + const T divisor = max_value - min_value; + + if (divisor == 0) + { + using RetType = decltype(T() / T2()); + return (in_value >= max_value) ? (RetType)1 : (RetType)0; + } + + return (in_value - min_value) / divisor; +} + +// 对数映射分贝值到非线性音量指示器 +inline float map_db_to_non_linear_volume_indicator(float dB, const float min_db, const float max_db, const float min_indicator, const float max_indicator) { + // 确保dB值在合理范围内 + dB = std::max(min_db, std::min(dB, max_db)); + + // 转换dB值为正数,以便于映射,并计算指数 + const float normalized_db = dB - min_db; + const float normalized_max_db = max_db - min_db; + + // 计算对数映射的指数部分 + const float exp_value = normalized_db == 0 ? 0 : (log10(normalized_db) / log10(normalized_max_db)); + + // 映射到指示器范围 + float indicator_value = exp_value * (max_indicator - min_indicator) + min_indicator; + + // 确保指示器值在合理范围内 + indicator_value = std::max(min_indicator, std::min(indicator_value, max_indicator)); + + return indicator_value; +} \ No newline at end of file diff --git a/Arona/widget/widget_mixer.cpp b/Arona/widget/widget_mixer.cpp index a249b66..f7ee638 100644 --- a/Arona/widget/widget_mixer.cpp +++ b/Arona/widget/widget_mixer.cpp @@ -8,6 +8,9 @@ widget_mixer_track::widget_mixer_track(mixer_track* in_mixer_track): mixer_track_(in_mixer_track) { name_ = mixer_track_->get_name(); peak_.resize(in_mixer_track->buffer.get_num_channels()); + for (auto& p: peak_) { + p = -90.f; + } peak_decay_.resize(in_mixer_track->buffer.get_num_channels()); } @@ -26,15 +29,20 @@ void widget_mixer_track::on_paint(ImGuiIO& io) { for (uint32_t i = 0; i < mixer_track_->ui_buffers.size(); i++) { auto& bu_buffer = mixer_track_->ui_buffers[i]; sample_count = std::min(sample_count, bu_buffer.Num()); - float temp_max = 0.f; - for (int i = 0; i < sample_count; ++i) { - float sample = bu_buffer.Pop(); - temp_max = std::max(temp_max, std::abs(sample)); - } + // float temp_max = 0.f; + std::vector samples(sample_count); + bu_buffer.Pop(samples.data(), sample_count); + const float rms = calculate_rms(samples); + const float dB = rms_2_db(rms, 1.0f); + // for (uint32_t i = 0; i < sample_count; ++i) { + // temp_max = std::max(temp_max, std::abs(samples[i])); + // } - update_peak(i, temp_max, io.DeltaTime); + update_peak(i, dB, io.DeltaTime); - draw_volume_bar(temp_max, peak_[i], ImVec2(5.0f, 100.0f), ""); + // const float peak = get_range_pct(-90.0f, 6.0f, peak_[i]); + + draw_volume_bar(dB, peak_[i], ImVec2(5.0f, 100.0f), ""); ImGui::SameLine(); } } @@ -50,8 +58,8 @@ void widget_mixer_track::update_peak(uint32_t channel, float new_peak, float del } current_decay -= delta_time; if (current_decay < 0.0f) { - current_peak -= delta_time * 0.5f; - current_peak = std::max(current_peak, 0.0f); + current_peak -= delta_time * 30.f; + current_peak = std::max(current_peak, -120.f); } }