summaryrefslogtreecommitdiff
path: root/Userland/Applications
diff options
context:
space:
mode:
authorkleines Filmröllchen <filmroellchen@serenityos.org>2022-03-03 22:32:45 +0100
committerAndreas Kling <kling@serenityos.org>2022-03-14 22:45:05 +0100
commit21266f42f10fba4a9259e53ab466051b2d724c85 (patch)
treece5b9757fd1d82aa643a51ebe08b55013c903b2c /Userland/Applications
parenta5d95aa6e86708ae12d9e25b5daca45cde851efa (diff)
downloadserenity-21266f42f10fba4a9259e53ab466051b2d724c85.zip
SoundPlayer: Use overlapping windows for bars visualization
For DSP reasons I can't explain myself (yet, sorry), short-time Fourier transform (STFT) is much more accurate and aesthetically pleasing when the windows that select the samples for STFT overlap. This implements that behavior by storing the previous samples and performing windowed FFT over both it as well as the current samples. This gives us 50% overlap between windows, a common standard that is nice to look at.
Diffstat (limited to 'Userland/Applications')
-rw-r--r--Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp14
-rw-r--r--Userland/Applications/SoundPlayer/BarsVisualizationWidget.h3
2 files changed, 13 insertions, 4 deletions
diff --git a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp
index 2bfe9fb4e7..810cf783a5 100644
--- a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp
+++ b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp
@@ -7,6 +7,7 @@
#include "BarsVisualizationWidget.h"
#include <AK/Math.h>
+#include <AK/TypedTransfer.h>
#include <LibDSP/FFT.h>
#include <LibDSP/Window.h>
#include <LibGUI/Event.h>
@@ -22,8 +23,14 @@ void BarsVisualizationWidget::render(GUI::PaintEvent& event, FixedArray<double>
painter.add_clip_rect(event.rect());
painter.fill_rect(frame_inner_rect(), Color::Black);
- for (size_t i = 0; i < fft_size; i++)
- m_fft_samples[i] = samples[i] * m_fft_window[i];
+ // First half of data is from previous iteration, second half is from now.
+ // This gives us fully overlapping windows, which result in more accurate and visually appealing STFT.
+ for (size_t i = 0; i < fft_size / 2; i++)
+ m_fft_samples[i] = m_previous_samples[i] * m_fft_window[i];
+ for (size_t i = 0; i < fft_size / 2; i++)
+ m_fft_samples[i + fft_size / 2] = samples[i] * m_fft_window[i + fft_size / 2];
+
+ AK::TypedTransfer<double>::copy(m_previous_samples.data(), samples.data(), samples.size());
LibDSP::fft(m_fft_samples.span(), false);
@@ -74,7 +81,8 @@ BarsVisualizationWidget::BarsVisualizationWidget()
m_fft_window = LibDSP::Window<double>::hann<fft_size>();
- MUST(set_render_sample_count(fft_size));
+ // As we use full-overlapping windows, the passed-in data is only half the size of one FFT operation.
+ MUST(set_render_sample_count(fft_size / 2));
}
void BarsVisualizationWidget::context_menu_event(GUI::ContextMenuEvent& event)
diff --git a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h
index ae44cc3253..d2bd539422 100644
--- a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h
+++ b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h
@@ -26,12 +26,13 @@ private:
void render(GUI::PaintEvent&, FixedArray<double> const&) override;
void context_menu_event(GUI::ContextMenuEvent& event) override;
- static constexpr size_t fft_size = 256;
+ static constexpr size_t fft_size = 512;
static constexpr size_t bar_count = 64;
static constexpr size_t values_per_bar = (fft_size / 2) / bar_count;
Array<Complex<double>, fft_size> m_fft_samples {};
Array<double, fft_size> m_fft_window {};
+ Array<double, fft_size / 2> m_previous_samples {};
Array<int, bar_count> m_gfx_falling_bars {};
bool m_is_using_last;
bool m_adjust_frequencies;