BarsVisualizationWidget.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /*
  2. * Copyright (c) 2021, Cesar Torres <shortanemoia@protonmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "BarsVisualizationWidget.h"
  7. #include "AudioAlgorithms.h"
  8. #include <AK/Complex.h>
  9. #include <LibGUI/Event.h>
  10. #include <LibGUI/Menu.h>
  11. #include <LibGUI/Painter.h>
  12. #include <LibGUI/Window.h>
  13. #include <math.h>
  14. u32 round_previous_power_of_2(u32 x);
  15. void BarsVisualizationWidget::paint_event(GUI::PaintEvent& event)
  16. {
  17. GUI::Frame::paint_event(event);
  18. GUI::Painter painter(*this);
  19. painter.add_clip_rect(event.rect());
  20. painter.fill_rect(frame_inner_rect(), Color::Black);
  21. if (m_sample_buffer.is_empty())
  22. return;
  23. fft(m_sample_buffer, false);
  24. double max = sqrt(m_sample_count * 2);
  25. double freq_bin = m_samplerate / m_sample_count;
  26. constexpr int group_count = 60;
  27. Vector<double, group_count> groups;
  28. groups.resize(group_count);
  29. if (m_gfx_falling_bars.size() != group_count) {
  30. m_gfx_falling_bars.resize(group_count);
  31. for (int& i : m_gfx_falling_bars)
  32. i = 0;
  33. }
  34. for (double& d : groups)
  35. d = 0.;
  36. int bins_per_group = ceil_div((m_sample_count - 1) / 2, group_count) * freq_bin;
  37. for (int i = 1; i < m_sample_count / 2; i++) {
  38. groups[(i * freq_bin) / bins_per_group] += fabs(m_sample_buffer.data()[i].real());
  39. }
  40. for (int i = 0; i < group_count; i++)
  41. groups[i] /= max * freq_bin / (m_adjust_frequencies ? (clamp(pow(M_E, (double)i / group_count * 3.) - 1.75, 1., 15.)) : 1.);
  42. const int horizontal_margin = 30;
  43. const int top_vertical_margin = 15;
  44. const int pixels_inbetween_groups = frame_inner_rect().width() > 350 ? 5 : 2;
  45. int pixel_per_group_width = (frame_inner_rect().width() - horizontal_margin * 2 - pixels_inbetween_groups * (group_count - 1)) / group_count;
  46. int max_height = frame_inner_rect().height() - top_vertical_margin;
  47. int current_xpos = horizontal_margin;
  48. for (int g = 0; g < group_count; g++) {
  49. m_gfx_falling_bars[g] = AK::min(clamp(max_height - (int)(groups[g] * max_height * 0.8), 0, max_height), m_gfx_falling_bars[g]);
  50. painter.fill_rect(Gfx::Rect(current_xpos, max_height - (int)(groups[g] * max_height * 0.8), pixel_per_group_width, (int)(groups[g] * max_height * 0.8)), Gfx::Color::from_rgb(0x95d437));
  51. painter.fill_rect(Gfx::Rect(current_xpos, m_gfx_falling_bars[g], pixel_per_group_width, 2), Gfx::Color::White);
  52. current_xpos += pixel_per_group_width + pixels_inbetween_groups;
  53. m_gfx_falling_bars[g] += 3;
  54. }
  55. m_is_using_last = false;
  56. }
  57. BarsVisualizationWidget::~BarsVisualizationWidget()
  58. {
  59. }
  60. BarsVisualizationWidget::BarsVisualizationWidget()
  61. : m_last_id(-1)
  62. , m_is_using_last(false)
  63. , m_adjust_frequencies(false)
  64. {
  65. m_context_menu = GUI::Menu::construct();
  66. m_context_menu->add_action(GUI::Action::create_checkable("Adjust frequency energy (for aesthetics)", [&](GUI::Action& action) {
  67. m_adjust_frequencies = action.is_checked();
  68. }));
  69. }
  70. // black magic from Hacker's delight
  71. u32 round_previous_power_of_2(u32 x)
  72. {
  73. x = x | (x >> 1);
  74. x = x | (x >> 2);
  75. x = x | (x >> 4);
  76. x = x | (x >> 8);
  77. x = x | (x >> 16);
  78. return x - (x >> 1);
  79. }
  80. void BarsVisualizationWidget::set_buffer(RefPtr<Audio::Buffer> buffer, int samples_to_use)
  81. {
  82. if (m_is_using_last)
  83. return;
  84. m_is_using_last = true;
  85. VERIFY(buffer->sample_count() >= 256);
  86. m_sample_count = round_previous_power_of_2(samples_to_use);
  87. m_sample_buffer.resize(m_sample_count);
  88. for (int i = 0; i < m_sample_count; i++) {
  89. m_sample_buffer.data()[i] = (fabs(buffer->samples()[i].left) + fabs(buffer->samples()[i].right)) / 2.;
  90. }
  91. update();
  92. }
  93. void BarsVisualizationWidget::set_buffer(RefPtr<Audio::Buffer> buffer)
  94. {
  95. if (buffer.is_null())
  96. return;
  97. if (m_last_id == buffer->id())
  98. return;
  99. set_buffer(buffer, buffer->sample_count());
  100. }
  101. void BarsVisualizationWidget::mousedown_event(GUI::MouseEvent& event)
  102. {
  103. Widget::mousedown_event(event);
  104. if (event.button() == GUI::Right) {
  105. m_context_menu->popup(event.position());
  106. }
  107. }
  108. void BarsVisualizationWidget::set_samplerate(int samplerate)
  109. {
  110. m_samplerate = samplerate;
  111. }