Skip to content

Commit

Permalink
SoundPlayer: Start working on a GUI sound player application
Browse files Browse the repository at this point in the history
This can play anything that AWavLoader can load (so obviously only WAV
files at the moment.)

It works by having a timer that wakes up every 100ms and tries to send
a sample buffer to the AudioServer. If our server-side queue is full
then we wait until the next timer iteration and try again.

We display the most recently enqueued sample buffer in a nice little
widget that just plots the samples in green-on-black. :^)
  • Loading branch information
awesomekling committed Sep 4, 2019
1 parent 6693e56 commit 1188a03
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Applications/SoundPlayer/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
include ../../Makefile.common

OBJS = \
SampleWidget.o \
main.o

APP = SoundPlayer

include ../Makefile.common
45 changes: 45 additions & 0 deletions Applications/SoundPlayer/SampleWidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "SampleWidget.h"
#include <LibAudio/ABuffer.h>
#include <LibGUI/GPainter.h>

SampleWidget::SampleWidget(GWidget* parent)
: GFrame(parent)
{
set_frame_shape(FrameShape::Container);
set_frame_shadow(FrameShadow::Sunken);
set_frame_thickness(2);
}

SampleWidget::~SampleWidget()
{
}

void SampleWidget::paint_event(GPaintEvent& event)
{
GFrame::paint_event(event);
GPainter painter(*this);
painter.add_clip_rect(event.rect());

painter.fill_rect(frame_inner_rect(), Color::Black);

if (!m_buffer)
return;

// FIXME: Right now we only display as many samples from the buffer as we can fit
// in the frame_inner_rect(). Maybe scale the samples or something?
int samples_to_draw = min(m_buffer->sample_count(), frame_inner_rect().width());
for (int x = 0; x < samples_to_draw; ++x) {
// FIXME: This might look nicer if drawn as lines.
auto& sample = m_buffer->samples()[x];
Point p = { x, frame_inner_rect().center().y() + (int)(sample.left * frame_inner_rect().height()) };
painter.set_pixel(p, Color::Green);
}
}

void SampleWidget::set_buffer(ABuffer* buffer)
{
if (m_buffer == buffer)
return;
m_buffer = buffer;
update();
}
19 changes: 19 additions & 0 deletions Applications/SoundPlayer/SampleWidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <LibGUI/GFrame.h>

class ABuffer;

class SampleWidget final : public GFrame {
C_OBJECT(SampleWidget)
public:
explicit SampleWidget(GWidget* parent);
virtual ~SampleWidget() override;

void set_buffer(ABuffer*);

private:
virtual void paint_event(GPaintEvent&) override;

RefPtr<ABuffer> m_buffer;
};
72 changes: 72 additions & 0 deletions Applications/SoundPlayer/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include "SampleWidget.h"
#include <LibAudio/ABuffer.h>
#include <LibAudio/AClientConnection.h>
#include <LibAudio/AWavLoader.h>
#include <LibCore/CTimer.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <stdio.h>

int main(int argc, char** argv)
{
if (argc != 2) {
printf("usage: %s <wav-file>\n", argv[0]);
return 0;
}

GApplication app(argc, argv);

String path = argv[1];
AWavLoader loader(path);

if (loader.has_error()) {
fprintf(stderr, "Failed to load WAV file: %s (%s)\n", path.characters(), loader.error_string());
return 1;
}

AClientConnection audio_client;
audio_client.handshake();

auto* window = new GWindow;
window->set_title("SoundPlayer");
window->set_rect(300, 300, 300, 200);

auto* widget = new GWidget;
window->set_main_widget(widget);

widget->set_fill_with_background_color(true);
widget->set_layout(make<GBoxLayout>(Orientation::Vertical));
widget->layout()->set_margins({ 2, 2, 2, 2 });

auto* sample_widget = new SampleWidget(widget);

auto* button = new GButton("Quit", widget);
button->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
button->set_preferred_size(0, 20);
button->on_click = [&](auto&) {
app.quit();
};

auto next_sample_buffer = loader.get_more_samples();

new CTimer(100, [&] {
if (!next_sample_buffer) {
sample_widget->set_buffer(nullptr);
return;
}
bool enqueued = audio_client.try_enqueue(*next_sample_buffer);
if (!enqueued)
return;
sample_widget->set_buffer(next_sample_buffer);
next_sample_buffer = loader.get_more_samples(16 * KB);
if (!next_sample_buffer) {
dbg() << "Exhausted samples :^)";
}
});

window->show();
return app.exec();
}
2 changes: 2 additions & 0 deletions Kernel/build-root-filesystem.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ cp ../Applications/Piano/Piano mnt/bin/Piano
cp ../Applications/SystemDialog/SystemDialog mnt/bin/SystemDialog
cp ../Applications/ChanViewer/ChanViewer mnt/bin/ChanViewer
cp ../Applications/Calculator/Calculator mnt/bin/Calculator
cp ../Applications/SoundPlayer/SoundPlayer mnt/bin/SoundPlayer
cp ../Demos/HelloWorld/HelloWorld mnt/bin/HelloWorld
cp ../Demos/HelloWorld2/HelloWorld2 mnt/bin/HelloWorld2
cp ../Demos/RetroFetch/RetroFetch mnt/bin/RetroFetch
Expand Down Expand Up @@ -123,6 +124,7 @@ ln -s SystemDialog mnt/bin/sd
ln -s ChanViewer mnt/bin/cv
ln -s Calculator mnt/bin/calc
ln -s Inspector mnt/bin/ins
ln -s SoundPlayer mnt/bin/sp
echo "done"

# Run local sync script, if it exists
Expand Down
1 change: 1 addition & 0 deletions Kernel/makeall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ build_targets="$build_targets ../Applications/SystemMonitor"
build_targets="$build_targets ../Applications/Taskbar"
build_targets="$build_targets ../Applications/Terminal"
build_targets="$build_targets ../Applications/TextEditor"
build_targets="$build_targets ../Applications/SoundPlayer"

build_targets="$build_targets ../Demos/Fire"
build_targets="$build_targets ../Demos/HelloWorld"
Expand Down

0 comments on commit 1188a03

Please sign in to comment.