소스 검색

SoundPlayer: Start working on a GUI sound player application

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. :^)
Andreas Kling 5 년 전
부모
커밋
1188a036e9

+ 9 - 0
Applications/SoundPlayer/Makefile

@@ -0,0 +1,9 @@
+include ../../Makefile.common
+
+OBJS = \
+    SampleWidget.o \
+    main.o
+
+APP = SoundPlayer
+
+include ../Makefile.common

+ 45 - 0
Applications/SoundPlayer/SampleWidget.cpp

@@ -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 - 0
Applications/SoundPlayer/SampleWidget.h

@@ -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 - 0
Applications/SoundPlayer/main.cpp

@@ -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 - 0
Kernel/build-root-filesystem.sh

@@ -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
@@ -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

+ 1 - 0
Kernel/makeall.sh

@@ -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"