VolumeBar

This commit is contained in:
2024-03-04 13:32:14 +08:00
parent 4e9f716931
commit 50cc2bb468
3 changed files with 79 additions and 15 deletions

View File

@@ -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));
}

View File

@@ -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<float>& 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<class T, class T2>
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;
}

View File

@@ -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<float> 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);
}
}