Piano: Cache buffers in Track and WaveWidget
The Track itself caches the Samples after each processing step which allows it to be queried without the need to process it every time. This result is queried by the WaveWidget which then caches the result to prevent unnecessary heap allocations every paint event.
This commit is contained in:
parent
885e35e92c
commit
413e212ea8
Notes:
sideshowbarker
2024-07-17 07:06:47 +09:00
Author: https://github.com/xZise Commit: https://github.com/SerenityOS/serenity/commit/413e212ea8 Pull-request: https://github.com/SerenityOS/serenity/pull/17350 Reviewed-by: https://github.com/gmta ✅ Reviewed-by: https://github.com/kleinesfilmroellchen ✅
5 changed files with 51 additions and 9 deletions
|
@ -41,6 +41,7 @@ ErrorOr<void> MainWidget::initialize()
|
|||
|
||||
m_wave_widget = TRY(try_add<WaveWidget>(m_track_manager));
|
||||
m_wave_widget->set_fixed_height(100);
|
||||
TRY(m_wave_widget->set_sample_size(sample_count));
|
||||
|
||||
m_tab_widget = TRY(try_add<GUI::TabWidget>());
|
||||
m_roll_widget = TRY(m_tab_widget->try_add_tab<RollWidget>(TRY("Piano Roll"_string), m_track_manager));
|
||||
|
|
|
@ -36,20 +36,19 @@ void WaveWidget::paint_event(GUI::PaintEvent& event)
|
|||
|
||||
Color left_wave_color = left_wave_colors[m_track_manager.current_track()->synth()->wave()];
|
||||
Color right_wave_color = right_wave_colors[m_track_manager.current_track()->synth()->wave()];
|
||||
// FIXME: We can't get the last buffer from the track manager anymore
|
||||
auto buffer = FixedArray<Audio::Sample>::must_create_but_fixme_should_propagate_errors(sample_count);
|
||||
double width_scale = static_cast<double>(frame_inner_rect().width()) / buffer.size();
|
||||
m_track_manager.current_track()->write_cached_signal_to(m_samples.span());
|
||||
double width_scale = static_cast<double>(frame_inner_rect().width()) / m_samples.size();
|
||||
|
||||
auto const maximum = Audio::Sample::max_range(buffer.span());
|
||||
auto const maximum = Audio::Sample::max_range(m_samples.span());
|
||||
int prev_x = 0;
|
||||
int prev_y_left = sample_to_y(buffer[0].left, maximum.left);
|
||||
int prev_y_right = sample_to_y(buffer[0].right, maximum.right);
|
||||
int prev_y_left = sample_to_y(m_samples[0].left, maximum.left);
|
||||
int prev_y_right = sample_to_y(m_samples[0].right, maximum.right);
|
||||
painter.set_pixel({ prev_x, prev_y_left }, left_wave_color);
|
||||
painter.set_pixel({ prev_x, prev_y_right }, right_wave_color);
|
||||
|
||||
for (size_t x = 1; x < buffer.size(); ++x) {
|
||||
int y_left = sample_to_y(buffer[x].left, maximum.left);
|
||||
int y_right = sample_to_y(buffer[x].right, maximum.right);
|
||||
for (size_t x = 1; x < m_samples.size(); ++x) {
|
||||
int y_left = sample_to_y(m_samples[x].left, maximum.left);
|
||||
int y_right = sample_to_y(m_samples[x].right, maximum.right);
|
||||
|
||||
Gfx::IntPoint point1_left(prev_x * width_scale, prev_y_left);
|
||||
Gfx::IntPoint point2_left(x * width_scale, y_left);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FixedArray.h>
|
||||
#include <LibAudio/Sample.h>
|
||||
#include <LibGUI/Frame.h>
|
||||
|
||||
|
@ -18,6 +19,12 @@ class WaveWidget final : public GUI::Frame {
|
|||
public:
|
||||
virtual ~WaveWidget() override = default;
|
||||
|
||||
ErrorOr<void> set_sample_size(size_t sample_size)
|
||||
{
|
||||
TRY(m_samples.try_resize(sample_size));
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
// Scales the sample-y value down by a bit, so that it doesn't look like it is clipping.
|
||||
static constexpr float rescale_factor = 1.2f;
|
||||
|
@ -29,4 +36,5 @@ private:
|
|||
int sample_to_y(float sample, float sample_max) const;
|
||||
|
||||
TrackManager& m_track_manager;
|
||||
Vector<Audio::Sample> m_samples;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibDSP/Music.h>
|
||||
#include <LibDSP/Processor.h>
|
||||
#include <LibDSP/Track.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace DSP {
|
||||
|
||||
|
@ -64,6 +65,12 @@ bool NoteTrack::check_processor_chain_valid() const
|
|||
ErrorOr<void> Track::resize_internal_buffers_to(size_t buffer_size)
|
||||
{
|
||||
m_secondary_sample_buffer = TRY(FixedArray<Sample>::create(buffer_size));
|
||||
FixedArray<Sample> cache = TRY(FixedArray<Sample>::create(buffer_size));
|
||||
bool false_variable = false;
|
||||
while (!m_sample_lock.compare_exchange_strong(false_variable, true))
|
||||
usleep(1);
|
||||
m_cached_sample_buffer.swap(cache);
|
||||
m_sample_lock.store(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -92,6 +99,25 @@ void Track::current_signal(FixedArray<Sample>& output_signal)
|
|||
VERIFY(output_signal.size() == source_signal->get<FixedArray<Sample>>().size());
|
||||
// The last processor is the fixed mastering processor. This can write directly to the output data. We also just trust this processor that it does the right thing :^)
|
||||
m_track_mastering->process_to_fixed_array(*source_signal, output_signal);
|
||||
|
||||
bool false_variable = false;
|
||||
if (m_sample_lock.compare_exchange_strong(false_variable, true)) {
|
||||
AK::TypedTransfer<Sample>::copy(m_cached_sample_buffer.data(), output_signal.data(), m_cached_sample_buffer.size());
|
||||
m_sample_lock.store(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::write_cached_signal_to(Span<Sample> output_signal)
|
||||
{
|
||||
bool false_variable = false;
|
||||
while (!m_sample_lock.compare_exchange_strong(false_variable, true)) {
|
||||
usleep(1);
|
||||
}
|
||||
VERIFY(output_signal.size() == m_cached_sample_buffer.size());
|
||||
|
||||
AK::TypedTransfer<Sample>::copy(output_signal.data(), m_cached_sample_buffer.data(), m_cached_sample_buffer.size());
|
||||
|
||||
m_sample_lock.store(false);
|
||||
}
|
||||
|
||||
void NoteTrack::compute_current_clips_signal()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/DisjointChunks.h>
|
||||
#include <AK/FixedArray.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/Weakable.h>
|
||||
|
@ -31,6 +32,8 @@ public:
|
|||
// Creates the current signal of the track by processing current note or audio data through the processing chain.
|
||||
void current_signal(FixedArray<Sample>& output_signal);
|
||||
|
||||
void write_cached_signal_to(Span<Sample> output_signal);
|
||||
|
||||
// We are informed of an audio buffer size change. This happens off-audio-thread so we can allocate.
|
||||
ErrorOr<void> resize_internal_buffers_to(size_t buffer_size);
|
||||
|
||||
|
@ -66,6 +69,11 @@ protected:
|
|||
Signal m_secondary_sample_buffer { FixedArray<Sample> {} };
|
||||
// A note buffer possibly used by the processor chain.
|
||||
Signal m_secondary_note_buffer { RollNotes {} };
|
||||
|
||||
private:
|
||||
Atomic<bool> m_sample_lock;
|
||||
|
||||
FixedArray<Sample> m_cached_sample_buffer = {};
|
||||
};
|
||||
|
||||
class NoteTrack final : public Track {
|
||||
|
|
Loading…
Add table
Reference in a new issue