diff --git a/Userland/Applications/Piano/MainWidget.cpp b/Userland/Applications/Piano/MainWidget.cpp index a2ade38d622..8b36f9997ea 100644 --- a/Userland/Applications/Piano/MainWidget.cpp +++ b/Userland/Applications/Piano/MainWidget.cpp @@ -41,6 +41,7 @@ ErrorOr MainWidget::initialize() m_wave_widget = TRY(try_add(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()); m_roll_widget = TRY(m_tab_widget->try_add_tab(TRY("Piano Roll"_string), m_track_manager)); diff --git a/Userland/Applications/Piano/WaveWidget.cpp b/Userland/Applications/Piano/WaveWidget.cpp index cdb6b1942c3..cf8542a090f 100644 --- a/Userland/Applications/Piano/WaveWidget.cpp +++ b/Userland/Applications/Piano/WaveWidget.cpp @@ -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::must_create_but_fixme_should_propagate_errors(sample_count); - double width_scale = static_cast(frame_inner_rect().width()) / buffer.size(); + m_track_manager.current_track()->write_cached_signal_to(m_samples.span()); + double width_scale = static_cast(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); diff --git a/Userland/Applications/Piano/WaveWidget.h b/Userland/Applications/Piano/WaveWidget.h index 3440f5be897..3b56d5de3e2 100644 --- a/Userland/Applications/Piano/WaveWidget.h +++ b/Userland/Applications/Piano/WaveWidget.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include @@ -18,6 +19,12 @@ class WaveWidget final : public GUI::Frame { public: virtual ~WaveWidget() override = default; + ErrorOr 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 m_samples; }; diff --git a/Userland/Libraries/LibDSP/Track.cpp b/Userland/Libraries/LibDSP/Track.cpp index 2e2d784efec..4e0deb64467 100644 --- a/Userland/Libraries/LibDSP/Track.cpp +++ b/Userland/Libraries/LibDSP/Track.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace DSP { @@ -64,6 +65,12 @@ bool NoteTrack::check_processor_chain_valid() const ErrorOr Track::resize_internal_buffers_to(size_t buffer_size) { m_secondary_sample_buffer = TRY(FixedArray::create(buffer_size)); + FixedArray cache = TRY(FixedArray::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& output_signal) VERIFY(output_signal.size() == source_signal->get>().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::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 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::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() diff --git a/Userland/Libraries/LibDSP/Track.h b/Userland/Libraries/LibDSP/Track.h index b75d59dba54..372aef0819f 100644 --- a/Userland/Libraries/LibDSP/Track.h +++ b/Userland/Libraries/LibDSP/Track.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -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& output_signal); + void write_cached_signal_to(Span output_signal); + // We are informed of an audio buffer size change. This happens off-audio-thread so we can allocate. ErrorOr resize_internal_buffers_to(size_t buffer_size); @@ -66,6 +69,11 @@ protected: Signal m_secondary_sample_buffer { FixedArray {} }; // A note buffer possibly used by the processor chain. Signal m_secondary_note_buffer { RollNotes {} }; + +private: + Atomic m_sample_lock; + + FixedArray m_cached_sample_buffer = {}; }; class NoteTrack final : public Track {