- Статус
- Оффлайн
- Регистрация
- 1 Июн 2018
- Сообщения
- 706
- Реакции
- 152
Выкладываю исходник саундбара для ImGui на C++.
В качестве примера — видео, где видно, как работает саундбар на музыке но без музыки.
Розыгрыш:
Если угадаете, что за песня играет на видео, скину победителю скины для CS2 в Steam на 500 рублей.
Условия:
— Два варианта в одном сообщении. Писать можете сколько угодно раз.
— Ответы отправляйте мне под хайдом в этой теме.
— Кто первый угадает — тому скромный приз скинами в Steam(около 500+ рублей).
Видео:
В качестве примера — видео, где видно, как работает саундбар на музыке но без музыки.
Розыгрыш:
Если угадаете, что за песня играет на видео, скину победителю скины для CS2 в Steam на 500 рублей.
Условия:
— Два варианта в одном сообщении. Писать можете сколько угодно раз.
— Ответы отправляйте мне под хайдом в этой теме.
— Кто первый угадает — тому скромный приз скинами в Steam(около 500+ рублей).
Видео:
AudioProcessor.h:
#pragma once
#include <vector>
#define MINIAUDIO_IMPLEMENTATION
#include <miniaudio.h>
class AudioProcessor {
public:
AudioProcessor();
~AudioProcessor();
void Update();
const std::vector<float>& GetBands() const;
private:
std::vector<float> bands;
void ProcessFFT(const float* samples, size_t count);
};
AudioProcessor.cpp:
#include "AudioProcessor.h"
#include <miniaudio.h>
#include <kissfft/kiss_fft.h>
#include <cmath>
#include <cstring>
#include <stdexcept>
static const int FFT_SIZE = 1024;
static ma_context context;
static ma_device device;
static std::vector<float> audioBuffer;
static std::vector<float> windowedSamples;
static kiss_fft_cfg fft_cfg = nullptr;
AudioProcessor::AudioProcessor() : bands(6, 0.0f) {
ma_context_init(NULL, 0, NULL, &context);
ma_device_config config = ma_device_config_init(ma_device_type_loopback);
config.capture.format = ma_format_f32;
config.capture.channels = 1;
config.sampleRate = 48000;
config.dataCallback = [](ma_device* d, void* out, const void* in, ma_uint32 frameCount) {
const float* fIn = (const float*)in;
for (ma_uint32 i = 0; i < frameCount; ++i)
audioBuffer.push_back(fIn[i]);
if (audioBuffer.size() > FFT_SIZE * 4)
audioBuffer.erase(audioBuffer.begin(), audioBuffer.end() - FFT_SIZE * 4);
};
config.pUserData = nullptr;
if (ma_device_init(&context, &config, &device) != MA_SUCCESS) {
throw std::runtime_error("Failed to initialize loopback device");
}
fft_cfg = kiss_fft_alloc(FFT_SIZE, 0, NULL, NULL);
audioBuffer.reserve(FFT_SIZE * 4);
windowedSamples.resize(FFT_SIZE);
ma_device_start(&device);
}
AudioProcessor::~AudioProcessor() {
ma_device_uninit(&device);
ma_context_uninit(&context);
if (fft_cfg) free(fft_cfg);
}
void AudioProcessor::Update() {
if (audioBuffer.size() < FFT_SIZE) return;
std::copy(audioBuffer.end() - FFT_SIZE, audioBuffer.end(), windowedSamples.begin());
for (int i = 0; i < FFT_SIZE; ++i) {
windowedSamples[i] *= 0.5f * (1.0f - cosf(2.0f * 3.14159f * i / (FFT_SIZE - 1)));
}
ProcessFFT(windowedSamples.data(), FFT_SIZE);
}
void AudioProcessor::ProcessFFT(const float* samples, size_t count) {
std::vector<kiss_fft_cpx> in(count), out(count);
for (size_t i = 0; i < count; ++i) {
in[i].r = samples[i];
in[i].i = 0.0f;
}
kiss_fft(fft_cfg, in.data(), out.data());
std::vector<float> magnitudes(count / 2);
for (size_t i = 0; i < count / 2; ++i) {
magnitudes[i] = sqrtf(out[i].r * out[i].r + out[i].i * out[i].i);
}
// 6 полос: низ → верх
int bandsMap[6][2] = {
{ 1, 4 }, // суббас
{ 5, 15 }, // бас
{ 16, 40 }, // нижняя середина
{ 41, 100 }, // середина
{ 101, 200 },// верхняя середина
{ 201, 400 } // высокие
};
for (int b = 0; b < 6; ++b) {
float sum = 0.0f;
int from = bandsMap[b][0], to = bandsMap[b][1];
for (int i = from; i <= to && i < magnitudes.size(); ++i)
sum += magnitudes[i];
bands[b] = sum / (to - from + 1);
}
}
const std::vector<float>& AudioProcessor::GetBands() const {
return bands;
}
soundbar begin:
should be called outside of cycle
AudioProcessor audio;
should be called in while
audio.Update();
const auto& bands = audio.GetBands();
{
{
ImGui::Begin("Audio Visualizer");
{
ImVec2 window_origin = ImGui::GetCursorScreenPos();
ImVec2 visualizer_size = ImVec2(752, 752);
ImRect visualizer_bb(window_origin, window_origin + visualizer_size);
const float visualizer_padding = 6.0f;
ImRect visualizer_inner_bb(visualizer_bb.Min + ImVec2(visualizer_padding, visualizer_padding), visualizer_bb.Max - ImVec2(visualizer_padding, visualizer_padding));
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddRectFilled(visualizer_bb.Min, visualizer_bb.Max, IM_COL32(20, 20, 30, 0), 4.0f);
draw_list->AddRect(visualizer_inner_bb.Min, visualizer_inner_bb.Max, IM_COL32(100, 100, 150, 0), 4.0f);
float visualizer_center_y = (visualizer_inner_bb.Min.y + visualizer_inner_bb.Max.y) * 0.5f;
const float bar_max_height = (visualizer_inner_bb.GetHeight() * 0.5f) - 2.0f;
const int bars_count = 6;
const float bar_width = 22.0f;
float visualizer_inner_width = visualizer_inner_bb.GetWidth();
float bar_spacing = (visualizer_inner_width - bars_count * bar_width) / (bars_count + 1);
static std::unordered_map<ImGuiID, float> current_bar_heights;
static std::unordered_map<ImGuiID, float> bar_peak_heights;
static std::unordered_map<ImGuiID, float> bar_fall_speeds;
static float global_signal_max = 0.1f;
float current_signal_max = 0.0f;
for (int bar_index = 0; bar_index < bars_count; ++bar_index) {
current_signal_max = ImMax(current_signal_max, bands[bar_index]);
}
global_signal_max = ImMax(global_signal_max * 0.997f, current_signal_max * 1.05f);
const float band_sensitivity_correction[bars_count] = { 0.6f, 0.8f, 1.0f, 1.2f, 1.5f, 2.0f };
for (int bar_index = 0; bar_index < bars_count; ++bar_index) {
ImGuiID bar_id = ImGui::GetID(bar_index);
float band_raw_amplitude = bands[bar_index];
float band_scaled_amplitude = (logf(1.0f + band_raw_amplitude * 20.0f) * band_sensitivity_correction[bar_index]) / (logf(1.0f + global_signal_max * 20.0f) + 0.001f);
float bar_target_height = ImClamp(band_scaled_amplitude * bar_max_height, 2.0f, bar_max_height);
if (!current_bar_heights.count(bar_id)) {
current_bar_heights[bar_id] = bar_target_height;
bar_peak_heights[bar_id] = bar_target_height;
bar_fall_speeds[bar_id] = 0.3f;
}
float& bar_current_height = current_bar_heights[bar_id];
float& bar_peak_height = bar_peak_heights[bar_id];
float& bar_fall_speed = bar_fall_speeds[bar_id];
if (bar_target_height > bar_current_height) {
bar_current_height = ImLerp(bar_current_height, bar_target_height, 0.7f);
bar_peak_height = bar_target_height;
bar_fall_speed = 0.01f;
}
else {
bar_fall_speed = ImMin(bar_fall_speed + 0.02f, 0.95f);
bar_current_height = ImLerp(bar_current_height, bar_target_height, bar_fall_speed);
}
float bar_center_x = visualizer_inner_bb.Min.x + bar_spacing * (bar_index + 1) + bar_width * bar_index + bar_width * 0.5f;
ImVec2 bar_top_left(bar_center_x, visualizer_center_y - bar_current_height);
ImVec2 bar_bottom_right(bar_center_x, visualizer_center_y + bar_current_height);
float amplitude_ratio = bar_current_height / bar_max_height;
ImU32 bar_color = IM_COL32(100 + (int)(155 * amplitude_ratio), 150, 255 - (int)(155 * amplitude_ratio), 220);
draw_list->AddRectFilled(bar_top_left, bar_bottom_right + ImVec2(bar_width, 0), bar_color, bar_width);
}
ImGui::Dummy(visualizer_size);
}
ImGui::End();
}
}
Так же на видео отчётливо видно биты и тд тп.
Последнее редактирование: