serenity/Ports/xmp-cli/patches/0001-Add-support-for-Serenity-LibAudio.patch
Andrew Kaster 77e890b15e Meta+Documentation+Ports: Move from C++20 to C++23
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.
2024-05-06 11:46:28 -06:00

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