1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 21:07:34 +00:00

LibAudio+LibDSP: Switch samples to 32-bit float instead of 64-bit float

This has been overkill from the start, and it has been bugging me for a
long time. With this change, we're probably a bit slower on most
platforms but save huge amounts of space with all in-memory sample
datastructures.
This commit is contained in:
kleines Filmröllchen 2022-05-06 22:14:16 +02:00 committed by Linus Groh
parent 39c0f31009
commit 19a4b820c4
16 changed files with 329 additions and 329 deletions

View file

@ -19,7 +19,7 @@ static FixedArray<Audio::Sample> music_samples_to_buffer(Vector<Music::Sample>&
{
FixedArray<Audio::Sample> samples = MUST(FixedArray<Audio::Sample>::try_create(music_samples.size()));
for (size_t i = 0; i < music_samples.size(); ++i)
samples[i] = { static_cast<double>(music_samples[i].left) / AK::NumericLimits<i16>::max(), static_cast<double>(music_samples[i].right) / AK::NumericLimits<i16>::max() };
samples[i] = { static_cast<float>(music_samples[i].left) / AK::NumericLimits<i16>::max(), static_cast<float>(music_samples[i].right) / AK::NumericLimits<i16>::max() };
return samples;
}

View file

@ -18,7 +18,7 @@ public:
void start_new_file(StringView) override;
private:
void render(GUI::PaintEvent&, FixedArray<double> const&) override { }
void render(GUI::PaintEvent&, FixedArray<float> const&) override { }
void paint_event(GUI::PaintEvent&) override;
AlbumCoverVisualizationWidget() = default;
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> get_album_cover(StringView const filename);

View file

@ -15,7 +15,7 @@
#include <LibGUI/Painter.h>
#include <LibGUI/Window.h>
void BarsVisualizationWidget::render(GUI::PaintEvent& event, FixedArray<double> const& samples)
void BarsVisualizationWidget::render(GUI::PaintEvent& event, FixedArray<float> const& samples)
{
GUI::Frame::paint_event(event);
GUI::Painter painter(*this);
@ -30,44 +30,44 @@ void BarsVisualizationWidget::render(GUI::PaintEvent& event, FixedArray<double>
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());
AK::TypedTransfer<float>::copy(m_previous_samples.data(), samples.data(), samples.size());
LibDSP::fft(m_fft_samples.span(), false);
Array<double, bar_count> groups {};
Array<float, bar_count> groups {};
if (m_logarithmic_spectrum) {
auto const log_bar_size = static_cast<double>(bar_count) / AK::log2(fft_size);
auto const log_bar_size = static_cast<float>(bar_count) / AK::log2(fft_size);
for (size_t i = 0; i < bar_count; ++i) {
auto const bar_start = i == 0 ? 0 : static_cast<size_t>(floor(AK::pow(2., static_cast<double>(i) / log_bar_size)));
auto const bar_end = clamp(static_cast<size_t>(floor(AK::pow(2., static_cast<double>(i + 1) / log_bar_size))), bar_start + 1, cutoff);
auto const bar_start = i == 0 ? 0 : static_cast<size_t>(floor(AK::pow(2.f, static_cast<float>(i) / log_bar_size)));
auto const bar_end = clamp(static_cast<size_t>(floor(AK::pow(2.f, static_cast<float>(i + 1) / log_bar_size))), bar_start + 1, cutoff);
auto const values_in_bar = bar_end - bar_start;
for (size_t sample_index = bar_start; sample_index < bar_start + values_in_bar; sample_index++) {
double const magnitude = m_fft_samples[sample_index].magnitude();
float const magnitude = m_fft_samples[sample_index].magnitude();
groups[i] += magnitude;
}
groups[i] /= static_cast<double>(values_in_bar);
groups[i] /= static_cast<float>(values_in_bar);
}
} else {
static constexpr size_t values_per_bar = (fft_size / 2) / bar_count;
for (size_t i = 0; i < fft_size / 2; i += values_per_bar) {
double const magnitude = m_fft_samples[i].magnitude();
float const magnitude = m_fft_samples[i].magnitude();
groups[i / values_per_bar] = magnitude;
for (size_t j = 0; j < values_per_bar; j++) {
double const magnitude = m_fft_samples[i + j].magnitude();
float const magnitude = m_fft_samples[i + j].magnitude();
groups[i / values_per_bar] += magnitude;
}
groups[i / values_per_bar] /= values_per_bar;
}
}
double const max_peak_value = AK::sqrt(static_cast<double>(fft_size * 2));
float const max_peak_value = AK::sqrt(static_cast<float>(fft_size * 2));
for (size_t i = 0; i < bar_count; i++) {
groups[i] = AK::log(groups[i] + 1) / AK::log(max_peak_value);
if (m_adjust_frequencies)
groups[i] *= 1 + 2.0 * (static_cast<double>(i) - static_cast<double>(bar_count / 3)) / static_cast<double>(bar_count);
groups[i] *= 1 + 2.0f * (static_cast<float>(i) - bar_count / 3.0f) / static_cast<float>(bar_count);
}
int const horizontal_margin = 30;
@ -77,8 +77,8 @@ void BarsVisualizationWidget::render(GUI::PaintEvent& event, FixedArray<double>
int const max_height = frame_inner_rect().height() - top_vertical_margin;
int current_xpos = horizontal_margin;
for (size_t g = 0; g < bar_count; g++) {
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]);
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));
m_gfx_falling_bars[g] = AK::min(clamp(max_height - static_cast<int>(groups[g] * static_cast<float>(max_height) * 0.8f), 0, max_height), m_gfx_falling_bars[g]);
painter.fill_rect(Gfx::Rect(current_xpos, max_height - static_cast<int>(groups[g] * static_cast<float>(max_height) * 0.8f), pixel_per_group_width, static_cast<int>(groups[g] * max_height * 0.8f)), Gfx::Color::from_rgb(0x95d437));
painter.fill_rect(Gfx::Rect(current_xpos, m_gfx_falling_bars[g], pixel_per_group_width, 2), Gfx::Color::White);
current_xpos += pixel_per_group_width + pixels_inbetween_groups;
m_gfx_falling_bars[g] += 3;
@ -102,7 +102,7 @@ BarsVisualizationWidget::BarsVisualizationWidget()
logarithmic_spectrum_action->set_checked(true);
m_context_menu->add_action(logarithmic_spectrum_action);
m_fft_window = LibDSP::Window<double>::hann<fft_size>();
m_fft_window = LibDSP::Window<float>::hann<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));

View file

@ -22,7 +22,7 @@ public:
private:
BarsVisualizationWidget();
void render(GUI::PaintEvent&, FixedArray<double> const&) override;
void render(GUI::PaintEvent&, FixedArray<float> const&) override;
void context_menu_event(GUI::ContextMenuEvent& event) override;
static constexpr size_t fft_size = 512;
@ -30,9 +30,9 @@ private:
// Things become weird near the Nyquist limit. Just don't use that FFT data.
static constexpr size_t cutoff = fft_size - 32;
Array<Complex<double>, fft_size> m_fft_samples {};
Array<double, fft_size> m_fft_window {};
Array<double, fft_size / 2> m_previous_samples {};
Array<Complex<float>, fft_size> m_fft_samples {};
Array<float, fft_size> m_fft_window {};
Array<float, fft_size / 2> m_previous_samples {};
Array<int, bar_count> m_gfx_falling_bars {};
bool m_is_using_last;
bool m_adjust_frequencies;

View file

@ -14,7 +14,7 @@ SampleWidget::SampleWidget()
MUST(set_render_sample_count(512));
}
void SampleWidget::render(GUI::PaintEvent& event, FixedArray<double> const& samples)
void SampleWidget::render(GUI::PaintEvent& event, FixedArray<float> const& samples)
{
GUI::Frame::paint_event(event);
GUI::Painter painter(*this);

View file

@ -17,5 +17,5 @@ public:
private:
SampleWidget();
virtual void render(GUI::PaintEvent&, FixedArray<double> const& samples) override;
virtual void render(GUI::PaintEvent&, FixedArray<float> const& samples) override;
};

View file

@ -17,7 +17,7 @@ class VisualizationWidget : public GUI::Frame {
C_OBJECT_ABSTRACT(VisualizationWidget)
public:
virtual void render(GUI::PaintEvent&, FixedArray<double> const& samples) = 0;
virtual void render(GUI::PaintEvent&, FixedArray<float> const& samples) = 0;
void set_buffer(FixedArray<Audio::Sample> const& buffer)
{
@ -28,7 +28,7 @@ public:
m_sample_buffer.resize(buffer.size());
for (size_t i = 0; i < buffer.size(); i++)
m_sample_buffer.data()[i] = (buffer[i].left + buffer[i].right) / 2.;
m_sample_buffer.data()[i] = (buffer[i].left + buffer[i].right) / 2.f;
m_frame_count = 0;
}
@ -55,7 +55,7 @@ public:
if (buffer_position + m_render_buffer.size() >= m_sample_buffer.size())
buffer_position = m_sample_buffer.size() - m_render_buffer.size();
AK::TypedTransfer<double>::copy(m_render_buffer.data(), m_sample_buffer.span().slice(buffer_position).data(), m_render_buffer.size());
AK::TypedTransfer<float>::copy(m_render_buffer.data(), m_sample_buffer.span().slice(buffer_position).data(), m_render_buffer.size());
render(event, m_render_buffer);
}
@ -70,7 +70,7 @@ public:
ErrorOr<void> set_render_sample_count(size_t count)
{
auto new_buffer = TRY(FixedArray<double>::try_create(count));
auto new_buffer = TRY(FixedArray<float>::try_create(count));
m_render_buffer.swap(new_buffer);
return {};
}
@ -79,8 +79,8 @@ public:
protected:
int m_samplerate;
size_t m_frame_count;
Vector<double> m_sample_buffer;
FixedArray<double> m_render_buffer;
Vector<float> m_sample_buffer;
FixedArray<float> m_render_buffer;
static constexpr size_t REFRESH_TIME_MILLISECONDS = 30;