diff options
author | kleines Filmröllchen <filmroellchen@serenityos.org> | 2022-03-03 22:32:45 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-03-14 22:45:05 +0100 |
commit | 21266f42f10fba4a9259e53ab466051b2d724c85 (patch) | |
tree | ce5b9757fd1d82aa643a51ebe08b55013c903b2c /Userland/Applications | |
parent | a5d95aa6e86708ae12d9e25b5daca45cde851efa (diff) | |
download | serenity-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.cpp | 14 | ||||
-rw-r--r-- | Userland/Applications/SoundPlayer/BarsVisualizationWidget.h | 3 |
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; |