mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 19:47:44 +00:00
Piano: Add Play/Pause, Forward and Back buttons
Piano now has a toolbar allowing the playback to be paused, or to be stepped forward or back a note.
This commit is contained in:
parent
54c005754a
commit
74f1f2b5e2
10 changed files with 234 additions and 69 deletions
62
Userland/Applications/Piano/AudioPlayerLoop.cpp
Normal file
62
Userland/Applications/Piano/AudioPlayerLoop.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, kleines Filmröllchen <malu.bertsch@gmail.com>
|
||||||
|
* Copyright (c) 2021, JJ Roberts-White <computerfido@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AudioPlayerLoop.h"
|
||||||
|
|
||||||
|
#include "TrackManager.h"
|
||||||
|
|
||||||
|
// Converts Piano-internal data to an Audio::Buffer that AudioServer receives
|
||||||
|
static NonnullRefPtr<Audio::Buffer> music_samples_to_buffer(Array<Sample, sample_count> samples)
|
||||||
|
{
|
||||||
|
Vector<Audio::Frame, sample_count> frames;
|
||||||
|
frames.ensure_capacity(sample_count);
|
||||||
|
for (auto sample : samples) {
|
||||||
|
Audio::Frame frame = { sample.left / (double)NumericLimits<i16>::max(), sample.right / (double)NumericLimits<i16>::max() };
|
||||||
|
frames.unchecked_append(frame);
|
||||||
|
}
|
||||||
|
return Audio::Buffer::create_with_samples(frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioPlayerLoop::AudioPlayerLoop(TrackManager& track_manager, bool& need_to_write_wav, Audio::WavWriter& wav_writer)
|
||||||
|
: m_track_manager(track_manager)
|
||||||
|
, m_need_to_write_wav(need_to_write_wav)
|
||||||
|
, m_wav_writer(wav_writer)
|
||||||
|
{
|
||||||
|
m_audio_client = Audio::ClientConnection::construct();
|
||||||
|
m_audio_client->on_finish_playing_buffer = [this](int buffer_id) {
|
||||||
|
(void)buffer_id;
|
||||||
|
enqueue_audio();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayerLoop::enqueue_audio()
|
||||||
|
{
|
||||||
|
m_track_manager.fill_buffer(m_buffer);
|
||||||
|
NonnullRefPtr<Audio::Buffer> audio_buffer = music_samples_to_buffer(m_buffer);
|
||||||
|
m_audio_client->async_enqueue(audio_buffer);
|
||||||
|
|
||||||
|
// FIXME: This should be done somewhere else.
|
||||||
|
if (m_need_to_write_wav) {
|
||||||
|
m_need_to_write_wav = false;
|
||||||
|
m_track_manager.reset();
|
||||||
|
m_track_manager.set_should_loop(false);
|
||||||
|
do {
|
||||||
|
m_track_manager.fill_buffer(m_buffer);
|
||||||
|
m_wav_writer.write_samples(reinterpret_cast<u8*>(m_buffer.data()), buffer_size);
|
||||||
|
} while (m_track_manager.time());
|
||||||
|
m_track_manager.reset();
|
||||||
|
m_track_manager.set_should_loop(true);
|
||||||
|
m_wav_writer.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayerLoop::toggle_paused()
|
||||||
|
{
|
||||||
|
m_should_play_audio = !m_should_play_audio;
|
||||||
|
|
||||||
|
m_audio_client->set_paused(!m_should_play_audio);
|
||||||
|
}
|
38
Userland/Applications/Piano/AudioPlayerLoop.h
Normal file
38
Userland/Applications/Piano/AudioPlayerLoop.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, kleines Filmröllchen <malu.bertsch@gmail.com>
|
||||||
|
* Copyright (c) 2021, JJ Roberts-White <computerfido@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Music.h"
|
||||||
|
#include <LibAudio/ClientConnection.h>
|
||||||
|
#include <LibAudio/WavWriter.h>
|
||||||
|
#include <LibCore/Object.h>
|
||||||
|
|
||||||
|
class TrackManager;
|
||||||
|
|
||||||
|
// Wrapper class accepting custom events to advance the track playing and forward audio data to the system.
|
||||||
|
// This does not run on a separate thread, preventing IPC multithreading madness.
|
||||||
|
class AudioPlayerLoop : public Core::Object {
|
||||||
|
C_OBJECT(AudioPlayerLoop)
|
||||||
|
public:
|
||||||
|
AudioPlayerLoop(TrackManager& track_manager, bool& need_to_write_wav, Audio::WavWriter& wav_writer);
|
||||||
|
|
||||||
|
void enqueue_audio();
|
||||||
|
|
||||||
|
void toggle_paused();
|
||||||
|
bool is_playing() { return m_should_play_audio; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
TrackManager& m_track_manager;
|
||||||
|
Array<Sample, sample_count> m_buffer;
|
||||||
|
RefPtr<Audio::ClientConnection> m_audio_client;
|
||||||
|
|
||||||
|
bool m_should_play_audio = true;
|
||||||
|
|
||||||
|
bool& m_need_to_write_wav;
|
||||||
|
Audio::WavWriter& m_wav_writer;
|
||||||
|
};
|
|
@ -6,12 +6,14 @@ serenity_component(
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
|
AudioPlayerLoop.cpp
|
||||||
Track.cpp
|
Track.cpp
|
||||||
TrackManager.cpp
|
TrackManager.cpp
|
||||||
KeysWidget.cpp
|
KeysWidget.cpp
|
||||||
KnobsWidget.cpp
|
KnobsWidget.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
MainWidget.cpp
|
MainWidget.cpp
|
||||||
|
PlayerWidget.cpp
|
||||||
RollWidget.cpp
|
RollWidget.cpp
|
||||||
SamplerWidget.cpp
|
SamplerWidget.cpp
|
||||||
WaveWidget.cpp
|
WaveWidget.cpp
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "MainWidget.h"
|
#include "MainWidget.h"
|
||||||
#include "KeysWidget.h"
|
#include "KeysWidget.h"
|
||||||
#include "KnobsWidget.h"
|
#include "KnobsWidget.h"
|
||||||
|
#include "PlayerWidget.h"
|
||||||
#include "RollWidget.h"
|
#include "RollWidget.h"
|
||||||
#include "SamplerWidget.h"
|
#include "SamplerWidget.h"
|
||||||
#include "TrackManager.h"
|
#include "TrackManager.h"
|
||||||
|
@ -17,8 +18,9 @@
|
||||||
#include <LibGUI/Menu.h>
|
#include <LibGUI/Menu.h>
|
||||||
#include <LibGUI/TabWidget.h>
|
#include <LibGUI/TabWidget.h>
|
||||||
|
|
||||||
MainWidget::MainWidget(TrackManager& track_manager)
|
MainWidget::MainWidget(TrackManager& track_manager, AudioPlayerLoop& loop)
|
||||||
: m_track_manager(track_manager)
|
: m_track_manager(track_manager)
|
||||||
|
, m_audio_loop(loop)
|
||||||
{
|
{
|
||||||
set_layout<GUI::VerticalBoxLayout>();
|
set_layout<GUI::VerticalBoxLayout>();
|
||||||
layout()->set_spacing(2);
|
layout()->set_spacing(2);
|
||||||
|
@ -35,6 +37,8 @@ MainWidget::MainWidget(TrackManager& track_manager)
|
||||||
|
|
||||||
m_tab_widget->add_tab<SamplerWidget>("Sampler", track_manager);
|
m_tab_widget->add_tab<SamplerWidget>("Sampler", track_manager);
|
||||||
|
|
||||||
|
m_player_widget = add<PlayerWidget>(track_manager, loop);
|
||||||
|
|
||||||
m_keys_and_knobs_container = add<GUI::Widget>();
|
m_keys_and_knobs_container = add<GUI::Widget>();
|
||||||
m_keys_and_knobs_container->set_layout<GUI::HorizontalBoxLayout>();
|
m_keys_and_knobs_container->set_layout<GUI::HorizontalBoxLayout>();
|
||||||
m_keys_and_knobs_container->layout()->set_spacing(2);
|
m_keys_and_knobs_container->layout()->set_spacing(2);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
|
* Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
|
||||||
|
* Copyright (c) 2021, JJ Roberts-White <computerfido@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -10,12 +11,14 @@
|
||||||
#include "Music.h"
|
#include "Music.h"
|
||||||
#include <LibGUI/Widget.h>
|
#include <LibGUI/Widget.h>
|
||||||
|
|
||||||
|
class AudioPlayerLoop;
|
||||||
class TrackManager;
|
class TrackManager;
|
||||||
class WaveWidget;
|
class WaveWidget;
|
||||||
class RollWidget;
|
class RollWidget;
|
||||||
class SamplerWidget;
|
class SamplerWidget;
|
||||||
class KeysWidget;
|
class KeysWidget;
|
||||||
class KnobsWidget;
|
class KnobsWidget;
|
||||||
|
class PlayerWidget;
|
||||||
|
|
||||||
class MainWidget final : public GUI::Widget {
|
class MainWidget final : public GUI::Widget {
|
||||||
C_OBJECT(MainWidget)
|
C_OBJECT(MainWidget)
|
||||||
|
@ -28,7 +31,7 @@ public:
|
||||||
void set_octave_and_ensure_note_change(int);
|
void set_octave_and_ensure_note_change(int);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit MainWidget(TrackManager&);
|
explicit MainWidget(TrackManager&, AudioPlayerLoop&);
|
||||||
|
|
||||||
virtual void keydown_event(GUI::KeyEvent&) override;
|
virtual void keydown_event(GUI::KeyEvent&) override;
|
||||||
virtual void keyup_event(GUI::KeyEvent&) override;
|
virtual void keyup_event(GUI::KeyEvent&) override;
|
||||||
|
@ -41,6 +44,7 @@ private:
|
||||||
void turn_on_pressed_keys();
|
void turn_on_pressed_keys();
|
||||||
|
|
||||||
TrackManager& m_track_manager;
|
TrackManager& m_track_manager;
|
||||||
|
AudioPlayerLoop& m_audio_loop;
|
||||||
|
|
||||||
RefPtr<WaveWidget> m_wave_widget;
|
RefPtr<WaveWidget> m_wave_widget;
|
||||||
RefPtr<RollWidget> m_roll_widget;
|
RefPtr<RollWidget> m_roll_widget;
|
||||||
|
@ -49,6 +53,7 @@ private:
|
||||||
RefPtr<GUI::Widget> m_keys_and_knobs_container;
|
RefPtr<GUI::Widget> m_keys_and_knobs_container;
|
||||||
RefPtr<KeysWidget> m_keys_widget;
|
RefPtr<KeysWidget> m_keys_widget;
|
||||||
RefPtr<KnobsWidget> m_knobs_widget;
|
RefPtr<KnobsWidget> m_knobs_widget;
|
||||||
|
RefPtr<PlayerWidget> m_player_widget;
|
||||||
|
|
||||||
bool m_keys_pressed[key_code_count] { false };
|
bool m_keys_pressed[key_code_count] { false };
|
||||||
};
|
};
|
||||||
|
|
63
Userland/Applications/Piano/PlayerWidget.cpp
Normal file
63
Userland/Applications/Piano/PlayerWidget.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, JJ Roberts-White <computerfido@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PlayerWidget.h"
|
||||||
|
|
||||||
|
#include "AudioPlayerLoop.h"
|
||||||
|
#include "Music.h"
|
||||||
|
#include "TrackManager.h"
|
||||||
|
#include <LibGUI/BoxLayout.h>
|
||||||
|
#include <LibGUI/Button.h>
|
||||||
|
|
||||||
|
PlayerWidget::PlayerWidget(TrackManager& manager, AudioPlayerLoop& loop)
|
||||||
|
: m_track_manager(manager)
|
||||||
|
, m_audio_loop(loop)
|
||||||
|
{
|
||||||
|
set_layout<GUI::HorizontalBoxLayout>();
|
||||||
|
set_fill_with_background_color(true);
|
||||||
|
|
||||||
|
m_play_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png");
|
||||||
|
m_pause_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png");
|
||||||
|
m_back_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"); // Go back a note
|
||||||
|
m_next_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"); // Advance a note
|
||||||
|
|
||||||
|
m_play_button = add<GUI::Button>();
|
||||||
|
m_play_button->set_icon(*m_pause_icon);
|
||||||
|
m_play_button->set_fixed_width(30);
|
||||||
|
m_play_button->set_tooltip("Play/Pause playback");
|
||||||
|
m_play_button->set_focus_policy(GUI::FocusPolicy::NoFocus);
|
||||||
|
m_play_button->on_click = [this](unsigned) {
|
||||||
|
m_audio_loop.toggle_paused();
|
||||||
|
|
||||||
|
if (m_audio_loop.is_playing()) {
|
||||||
|
m_play_button->set_icon(*m_pause_icon);
|
||||||
|
} else {
|
||||||
|
m_play_button->set_icon(*m_play_icon);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
m_back_button = add<GUI::Button>();
|
||||||
|
m_back_button->set_icon(*m_back_icon);
|
||||||
|
m_back_button->set_fixed_width(30);
|
||||||
|
m_back_button->set_tooltip("Previous Note");
|
||||||
|
m_back_button->set_focus_policy(GUI::FocusPolicy::NoFocus);
|
||||||
|
m_back_button->on_click = [this](unsigned) {
|
||||||
|
m_track_manager.time_forward(-(sample_rate / (beats_per_minute / 60) / notes_per_beat));
|
||||||
|
};
|
||||||
|
|
||||||
|
m_next_button = add<GUI::Button>();
|
||||||
|
m_next_button->set_icon(*m_next_icon);
|
||||||
|
m_next_button->set_fixed_width(30);
|
||||||
|
m_next_button->set_tooltip("Next Note");
|
||||||
|
m_next_button->set_focus_policy(GUI::FocusPolicy::NoFocus);
|
||||||
|
m_next_button->on_click = [this](unsigned) {
|
||||||
|
m_track_manager.time_forward((sample_rate / (beats_per_minute / 60) / notes_per_beat));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerWidget::~PlayerWidget()
|
||||||
|
{
|
||||||
|
}
|
33
Userland/Applications/Piano/PlayerWidget.h
Normal file
33
Userland/Applications/Piano/PlayerWidget.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, JJ Roberts-White <computerfido@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGUI/Toolbar.h>
|
||||||
|
|
||||||
|
class AudioPlayerLoop;
|
||||||
|
class TrackManager;
|
||||||
|
|
||||||
|
class PlayerWidget final : public GUI::Toolbar {
|
||||||
|
C_OBJECT(PlayerWidget)
|
||||||
|
public:
|
||||||
|
virtual ~PlayerWidget() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit PlayerWidget(TrackManager&, AudioPlayerLoop&);
|
||||||
|
|
||||||
|
TrackManager& m_track_manager;
|
||||||
|
AudioPlayerLoop& m_audio_loop;
|
||||||
|
|
||||||
|
RefPtr<Gfx::Bitmap> m_play_icon;
|
||||||
|
RefPtr<Gfx::Bitmap> m_pause_icon;
|
||||||
|
RefPtr<Gfx::Bitmap> m_back_icon;
|
||||||
|
RefPtr<Gfx::Bitmap> m_next_icon;
|
||||||
|
|
||||||
|
RefPtr<GUI::Button> m_play_button;
|
||||||
|
RefPtr<GUI::Button> m_back_button;
|
||||||
|
RefPtr<GUI::Button> m_next_button;
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
|
* Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
|
||||||
|
* Copyright (c) 2021 JJ Roberts-White <computerfido@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -16,6 +17,17 @@ TrackManager::~TrackManager()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TrackManager::time_forward(int amount)
|
||||||
|
{
|
||||||
|
int new_value = (static_cast<int>(m_time) + amount) % roll_length;
|
||||||
|
|
||||||
|
if (new_value < 0) { // If the new time value is negaive add roll_length to wrap around
|
||||||
|
m_time = roll_length + new_value;
|
||||||
|
} else {
|
||||||
|
m_time = new_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TrackManager::fill_buffer(Span<Sample> buffer)
|
void TrackManager::fill_buffer(Span<Sample> buffer)
|
||||||
{
|
{
|
||||||
memset(buffer.data(), 0, buffer_size);
|
memset(buffer.data(), 0, buffer_size);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
|
* Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
|
||||||
|
* Copyright (c) 2021, JJ Roberts-White <computerfido@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -26,7 +27,9 @@ public:
|
||||||
Span<const Sample> buffer() const { return m_current_front_buffer; }
|
Span<const Sample> buffer() const { return m_current_front_buffer; }
|
||||||
int octave() const { return m_octave; }
|
int octave() const { return m_octave; }
|
||||||
int octave_base() const { return (m_octave - octave_min) * 12; }
|
int octave_base() const { return (m_octave - octave_min) * 12; }
|
||||||
|
|
||||||
int time() const { return m_time; }
|
int time() const { return m_time; }
|
||||||
|
void time_forward(int amount);
|
||||||
|
|
||||||
void fill_buffer(Span<Sample>);
|
void fill_buffer(Span<Sample>);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
|
* Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
|
||||||
* Copyright (c) 2021, kleines Filmröllchen <malu.bertsch@gmail.com>
|
* Copyright (c) 2021, kleines Filmröllchen <malu.bertsch@gmail.com>
|
||||||
|
* Copyright (c) 2021, JJ Roberts-White <computerfido@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "AudioPlayerLoop.h"
|
||||||
#include "MainWidget.h"
|
#include "MainWidget.h"
|
||||||
#include "TrackManager.h"
|
#include "TrackManager.h"
|
||||||
#include <AK/Array.h>
|
#include <AK/Array.h>
|
||||||
|
@ -26,65 +28,6 @@
|
||||||
#include <LibGfx/Bitmap.h>
|
#include <LibGfx/Bitmap.h>
|
||||||
#include <LibThreading/Thread.h>
|
#include <LibThreading/Thread.h>
|
||||||
|
|
||||||
// Converts Piano-internal data to an Audio::Buffer that AudioServer receives
|
|
||||||
static NonnullRefPtr<Audio::Buffer> music_samples_to_buffer(Array<Sample, sample_count> samples)
|
|
||||||
{
|
|
||||||
Vector<Audio::Frame, sample_count> frames;
|
|
||||||
frames.ensure_capacity(sample_count);
|
|
||||||
for (auto sample : samples) {
|
|
||||||
Audio::Frame frame = { sample.left / (double)NumericLimits<i16>::max(), sample.right / (double)NumericLimits<i16>::max() };
|
|
||||||
frames.unchecked_append(frame);
|
|
||||||
}
|
|
||||||
return Audio::Buffer::create_with_samples(frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper class accepting custom events to advance the track playing and forward audio data to the system.
|
|
||||||
// This does not run on a separate thread, preventing IPC multithreading madness.
|
|
||||||
class AudioPlayerLoop : public Core::Object {
|
|
||||||
C_OBJECT(AudioPlayerLoop)
|
|
||||||
public:
|
|
||||||
AudioPlayerLoop(TrackManager& track_manager, bool& need_to_write_wav, Audio::WavWriter& wav_writer)
|
|
||||||
: m_track_manager(track_manager)
|
|
||||||
, m_need_to_write_wav(need_to_write_wav)
|
|
||||||
, m_wav_writer(wav_writer)
|
|
||||||
{
|
|
||||||
m_audio_client = Audio::ClientConnection::construct();
|
|
||||||
m_audio_client->on_finish_playing_buffer = [this](int buffer_id) {
|
|
||||||
(void)buffer_id;
|
|
||||||
enqueue_audio();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void enqueue_audio()
|
|
||||||
{
|
|
||||||
m_track_manager.fill_buffer(m_buffer);
|
|
||||||
NonnullRefPtr<Audio::Buffer> audio_buffer = music_samples_to_buffer(m_buffer);
|
|
||||||
m_audio_client->async_enqueue(audio_buffer);
|
|
||||||
|
|
||||||
// FIXME: This should be done somewhere else.
|
|
||||||
if (m_need_to_write_wav) {
|
|
||||||
m_need_to_write_wav = false;
|
|
||||||
m_track_manager.reset();
|
|
||||||
m_track_manager.set_should_loop(false);
|
|
||||||
do {
|
|
||||||
m_track_manager.fill_buffer(m_buffer);
|
|
||||||
m_wav_writer.write_samples(reinterpret_cast<u8*>(m_buffer.data()), buffer_size);
|
|
||||||
} while (m_track_manager.time());
|
|
||||||
m_track_manager.reset();
|
|
||||||
m_track_manager.set_should_loop(true);
|
|
||||||
m_wav_writer.finalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
TrackManager& m_track_manager;
|
|
||||||
Array<Sample, sample_count> m_buffer;
|
|
||||||
RefPtr<Audio::ClientConnection> m_audio_client;
|
|
||||||
|
|
||||||
bool& m_need_to_write_wav;
|
|
||||||
Audio::WavWriter& m_wav_writer;
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (pledge("stdio thread rpath cpath wpath recvfd sendfd unix", nullptr) < 0) {
|
if (pledge("stdio thread rpath cpath wpath recvfd sendfd unix", nullptr) < 0) {
|
||||||
|
@ -96,14 +39,6 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
TrackManager track_manager;
|
TrackManager track_manager;
|
||||||
|
|
||||||
auto app_icon = GUI::Icon::default_icon("app-piano");
|
|
||||||
auto window = GUI::Window::construct();
|
|
||||||
auto& main_widget = window->set_main_widget<MainWidget>(track_manager);
|
|
||||||
window->set_title("Piano");
|
|
||||||
window->resize(840, 600);
|
|
||||||
window->set_icon(app_icon.bitmap_for_size(16));
|
|
||||||
window->show();
|
|
||||||
|
|
||||||
Audio::WavWriter wav_writer;
|
Audio::WavWriter wav_writer;
|
||||||
Optional<String> save_path;
|
Optional<String> save_path;
|
||||||
bool need_to_write_wav = false;
|
bool need_to_write_wav = false;
|
||||||
|
@ -112,6 +47,14 @@ int main(int argc, char** argv)
|
||||||
audio_loop->enqueue_audio();
|
audio_loop->enqueue_audio();
|
||||||
audio_loop->enqueue_audio();
|
audio_loop->enqueue_audio();
|
||||||
|
|
||||||
|
auto app_icon = GUI::Icon::default_icon("app-piano");
|
||||||
|
auto window = GUI::Window::construct();
|
||||||
|
auto& main_widget = window->set_main_widget<MainWidget>(track_manager, audio_loop);
|
||||||
|
window->set_title("Piano");
|
||||||
|
window->resize(840, 600);
|
||||||
|
window->set_icon(app_icon.bitmap_for_size(16));
|
||||||
|
window->show();
|
||||||
|
|
||||||
auto main_widget_updater = Core::Timer::construct(static_cast<int>((1 / 60.0) * 1000), [&] {
|
auto main_widget_updater = Core::Timer::construct(static_cast<int>((1 / 60.0) * 1000), [&] {
|
||||||
Core::EventLoop::current().post_event(main_widget, make<Core::CustomEvent>(0));
|
Core::EventLoop::current().post_event(main_widget, make<Core::CustomEvent>(0));
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue