Ports: Add xmp-cli (Extended Module Player)

This commit is contained in:
Julian Offenhäuser 2024-02-23 23:03:37 +01:00 committed by Tim Schumacher
parent d429b2ff6d
commit 9b1b8828b4
Notes: sideshowbarker 2024-07-16 23:03:06 +09:00
4 changed files with 260 additions and 0 deletions

View File

@ -335,6 +335,7 @@ This list is also available at [ports.serenityos.net](https://ports.serenityos.n
| [`x264`](x264/) | x264 | a8b68eb | https://www.videolan.org/developers/x264.html |
| [`x265`](x265/) | x265 | 3.5 | https://bitbucket.org/multicoreware/x265_git/wiki/Home |
| [`xash3d-fwgs`](xash3d-fwgs/) | Xash3D FWGS game engine | 2022.12.26 | https://github.com/FWGS/xash3d-fwgs |
| [`xmp-cli`](xmp-cli/) | Extended Module Player | 4.2.0 | https://github.com/libxmp/xmp-cli |
| [`xorriso`](xorriso/) | xorriso | 1.5.6 | https://www.gnu.org/software/xorriso |
| [`xz`](xz/) | xz | 5.4.4 | https://tukaani.org/xz/ |
| [`yasm`](yasm/) | Yasm Modular Assembler | 1.3.0 | https://yasm.tortall.net/ |

15
Ports/xmp-cli/package.sh Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env -S bash ../.port_include.sh
port='xmp-cli'
version='4.2.0'
workdir="xmp-${version}"
files=(
"https://github.com/libxmp/xmp-cli/releases/download/xmp-${version}/xmp-${version}.tar.gz#dc54513af9a4681029a1243fd0c9cdf153d813a1125de6c782926674285bc5ae"
)
useconfigure='true'
use_fresh_config_sub='false'
configopts=(
'--disable-alsa'
'--disable-oss'
'--disable-sndio'
)
depends=( 'libxmp' )

View File

@ -0,0 +1,237 @@
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++2a
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 -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

View File

@ -0,0 +1,7 @@
# Patches for xmp-cli on SerenityOS
## `0001-Add-support-for-Serenity-LibAudio.patch`
Add support for Serenity LibAudio