mirror of
https://github.com/SerenityOS/serenity
synced 2024-07-23 02:55:15 +00:00
![Andrew Kaster](/assets/img/avatar_default.png)
Now that oss-fuzz is on a clang commit > the 17.x release candidates, we can start looking at some shiny new features to enable.
238 lines
7.3 KiB
Diff
238 lines
7.3 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Julian=20Offenh=C3=A4user?= <offenhaeuser@protonmail.com>
|
|
Date: Fri, 23 Feb 2024 21:17:23 +0100
|
|
Subject: [PATCH] Add support for Serenity LibAudio
|
|
|
|
---
|
|
src/Makefile.am | 5 +-
|
|
src/sound.c | 1 +
|
|
src/sound.h | 1 +
|
|
src/sound_serenity.cpp | 157 +++++++++++++++++++++++++++++++++++++++++
|
|
src/xmp.conf | 2 +-
|
|
5 files changed, 164 insertions(+), 2 deletions(-)
|
|
create mode 100644 src/sound_serenity.cpp
|
|
|
|
diff --git a/src/Makefile.am b/src/Makefile.am
|
|
index b65eb85244acec722b2f240976f184f06db11a27..840845dc1f5d7459bfbb339071b817adc2507c6a 100644
|
|
--- a/src/Makefile.am
|
|
+++ b/src/Makefile.am
|
|
@@ -1,7 +1,7 @@
|
|
# -*- Makefile -*-
|
|
|
|
AM_CPPFLAGS = -DSYSCONFDIR=\"${sysconfdir}/${PACKAGE_NAME}\" ${LIBXMP_CFLAGS} \
|
|
- ${alsa_CFLAGS} ${pulseaudio_CFLAGS}
|
|
+ ${alsa_CFLAGS} ${pulseaudio_CFLAGS} -std=c++23
|
|
AM_CFLAGS = -Wall
|
|
|
|
bin_PROGRAMS = xmp
|
|
@@ -93,6 +93,9 @@ xmp_SOURCES += sound_dart.c
|
|
xmp_LDADD += -lmmpm2
|
|
endif
|
|
|
|
+xmp_SOURCES += sound_serenity.cpp
|
|
+xmp_LDADD += -lcore -lcoreminimal -lipc -laudio -lstdc++
|
|
+
|
|
man_MANS = xmp.1
|
|
|
|
pkgsysconfdir = ${sysconfdir}/${PACKAGE_NAME}
|
|
diff --git a/src/sound.c b/src/sound.c
|
|
index b6d0c971876a555e27a628b0517a8dd653f24dff..04935412baf8a17558dcc62914d4a5cc1a72c49d 100644
|
|
--- a/src/sound.c
|
|
+++ b/src/sound.c
|
|
@@ -73,6 +73,7 @@ void init_sound_drivers(void)
|
|
#ifdef SOUND_SB
|
|
register_sound_driver(&sound_sb);
|
|
#endif
|
|
+ register_sound_driver(&sound_serenity);
|
|
register_sound_driver(&sound_wav);
|
|
register_sound_driver(&sound_aiff);
|
|
register_sound_driver(&sound_file);
|
|
diff --git a/src/sound.h b/src/sound.h
|
|
index 153a6c7cb6f6f1431e00b5a10a8a36d3fe7ab7db..beebb83fa3cccdb53bdd2f6893307e476460373f 100644
|
|
--- a/src/sound.h
|
|
+++ b/src/sound.h
|
|
@@ -41,6 +41,7 @@ extern struct sound_driver sound_beos;
|
|
extern struct sound_driver sound_aix;
|
|
extern struct sound_driver sound_ahi;
|
|
extern struct sound_driver sound_sb;
|
|
+extern struct sound_driver sound_serenity;
|
|
|
|
#define parm_init(p) { char *token; for (; *(p); (p)++) { \
|
|
char s[80]; strncpy(s, *(p), 79); s[79] = 0; \
|
|
diff --git a/src/sound_serenity.cpp b/src/sound_serenity.cpp
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..172996990b7807e3eecf119557274febcc4f5d54
|
|
--- /dev/null
|
|
+++ b/src/sound_serenity.cpp
|
|
@@ -0,0 +1,157 @@
|
|
+#include "sound.h"
|
|
+
|
|
+#include <AK/OwnPtr.h>
|
|
+#include <AK/RefPtr.h>
|
|
+#include <LibAudio/ConnectionToServer.h>
|
|
+#include <LibAudio/Sample.h>
|
|
+#include <LibAudio/SampleFormats.h>
|
|
+
|
|
+static RefPtr<Audio::ConnectionToServer> client;
|
|
+static OwnPtr<Core::EventLoop> event_loop;
|
|
+
|
|
+static Array<Audio::Sample, Audio::AUDIO_BUFFER_SIZE> output_buffer {};
|
|
+static size_t output_buffer_samples_remaining { 0 };
|
|
+
|
|
+static int format;
|
|
+static int channels;
|
|
+static int bytes_per_sample;
|
|
+
|
|
+static int init(struct options* options)
|
|
+{
|
|
+ event_loop = make<Core::EventLoop>();
|
|
+ client = MUST(Audio::ConnectionToServer::try_create());
|
|
+
|
|
+ format = options->format;
|
|
+ channels = format & XMP_FORMAT_MONO ? 1 : 2;
|
|
+ bytes_per_sample = format & XMP_FORMAT_8BIT ? 1 : 2;
|
|
+
|
|
+ u32 sample_rate = options->rate;
|
|
+ client->set_self_sample_rate(sample_rate);
|
|
+
|
|
+ client->async_start_playback();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void deinit(void)
|
|
+{
|
|
+ client->die();
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+static double read_sample(void*& b)
|
|
+{
|
|
+ T sample = *(T*)b;
|
|
+ b += sizeof(T);
|
|
+
|
|
+ // Remap integer samples to normalized floating-point range of -1 to 1.
|
|
+ if constexpr (IsIntegral<T>) {
|
|
+ if constexpr (NumericLimits<T>::is_signed()) {
|
|
+ // Signed integer samples are centered around zero, so this division is enough.
|
|
+ return static_cast<double>(AK::convert_between_host_and_little_endian(sample)) / static_cast<double>(NumericLimits<T>::max());
|
|
+ } else {
|
|
+ // Unsigned integer samples, on the other hand, need to be shifted to center them around zero.
|
|
+ // The first division therefore remaps to the range 0 to 2.
|
|
+ return static_cast<double>(AK::convert_between_host_and_little_endian(sample)) / (static_cast<double>(NumericLimits<T>::max()) / 2.0) - 1.0;
|
|
+ }
|
|
+ } else {
|
|
+ return static_cast<double>(AK::convert_between_host_and_little_endian(sample));
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Build and write one tick (one PAL frame or 1/50 s in standard vblank
|
|
+ * timed mods) of audio data to the output device.
|
|
+ */
|
|
+static void play(void* b, int i)
|
|
+{
|
|
+ int samples_per_channel = i / (channels * bytes_per_sample);
|
|
+ int unsigned_samples = format & XMP_FORMAT_UNSIGNED;
|
|
+
|
|
+ auto const sleep_spec = Duration::from_nanoseconds(100).to_timespec();
|
|
+
|
|
+ while (samples_per_channel > 0) {
|
|
+ // Fill up the output buffer
|
|
+ auto const samples_to_process = min(samples_per_channel, Audio::AUDIO_BUFFER_SIZE - output_buffer_samples_remaining);
|
|
+
|
|
+ for (int i = 0; i < samples_to_process; ++i) {
|
|
+ float left, right;
|
|
+
|
|
+ auto convert_sample = [&]() -> float {
|
|
+ if (unsigned_samples) {
|
|
+ if (format & XMP_FORMAT_8BIT)
|
|
+ return read_sample<u8>(b);
|
|
+ else
|
|
+ return read_sample<u16>(b);
|
|
+ } else {
|
|
+ if (format & XMP_FORMAT_8BIT)
|
|
+ return read_sample<i8>(b);
|
|
+ else
|
|
+ return read_sample<i16>(b);
|
|
+ }
|
|
+ return 0.0f;
|
|
+ };
|
|
+
|
|
+ left = convert_sample();
|
|
+ if (format & XMP_FORMAT_MONO) {
|
|
+ right = left;
|
|
+ } else {
|
|
+ right = convert_sample();
|
|
+ }
|
|
+
|
|
+ output_buffer[output_buffer_samples_remaining + i] = Audio::Sample { left, right };
|
|
+ }
|
|
+ output_buffer_samples_remaining += samples_to_process;
|
|
+ samples_per_channel -= samples_to_process;
|
|
+
|
|
+ // Stop if we don't have enough samples to fill a buffer
|
|
+ if (output_buffer_samples_remaining < Audio::AUDIO_BUFFER_SIZE)
|
|
+ break;
|
|
+
|
|
+ // Try to enqueue our output buffer
|
|
+ for (;;) {
|
|
+ auto enqueue_result = client->realtime_enqueue(output_buffer);
|
|
+ if (!enqueue_result.is_error())
|
|
+ break;
|
|
+ if (enqueue_result.error() != Audio::AudioQueue::QueueStatus::Full)
|
|
+ return;
|
|
+
|
|
+ nanosleep(&sleep_spec, nullptr);
|
|
+ }
|
|
+ output_buffer_samples_remaining = 0;
|
|
+ }
|
|
+
|
|
+ // Pump our event loop - should just be the IPC call to start playback
|
|
+ for (;;) {
|
|
+ auto number_of_events_pumped = event_loop->pump(Core::EventLoop::WaitMode::PollForEvents);
|
|
+ if (number_of_events_pumped == 0)
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void flush(void)
|
|
+{
|
|
+}
|
|
+
|
|
+static void onpause(void)
|
|
+{
|
|
+}
|
|
+
|
|
+static void onresume(void)
|
|
+{
|
|
+}
|
|
+
|
|
+static char const* const help[] = {
|
|
+ NULL
|
|
+};
|
|
+
|
|
+struct sound_driver sound_serenity = {
|
|
+ "serenity",
|
|
+ "Serenity AudioServer",
|
|
+ help,
|
|
+ init,
|
|
+ deinit,
|
|
+ play,
|
|
+ flush,
|
|
+ onpause,
|
|
+ onresume
|
|
+};
|
|
diff --git a/src/xmp.conf b/src/xmp.conf
|
|
index 08166a0e7e5f2cefa69fc1c843ca27c5388cbdf1..fa9ace68299cb0c503276629b809b83edad5cf31 100644
|
|
--- a/src/xmp.conf
|
|
+++ b/src/xmp.conf
|
|
@@ -19,7 +19,7 @@
|
|
# driver = <name>
|
|
# Output device to be used
|
|
#
|
|
-#driver = alsa
|
|
+driver = serenity
|
|
|
|
|
|
# ALSA driver
|