diff --git a/Ports/AvailablePorts.md b/Ports/AvailablePorts.md index a094ffa4298..501a15d6e47 100644 --- a/Ports/AvailablePorts.md +++ b/Ports/AvailablePorts.md @@ -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/ | diff --git a/Ports/xmp-cli/package.sh b/Ports/xmp-cli/package.sh new file mode 100755 index 00000000000..be0661af75c --- /dev/null +++ b/Ports/xmp-cli/package.sh @@ -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' ) diff --git a/Ports/xmp-cli/patches/0001-Add-support-for-Serenity-LibAudio.patch b/Ports/xmp-cli/patches/0001-Add-support-for-Serenity-LibAudio.patch new file mode 100644 index 00000000000..8897864d1a7 --- /dev/null +++ b/Ports/xmp-cli/patches/0001-Add-support-for-Serenity-LibAudio.patch @@ -0,0 +1,237 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Julian=20Offenh=C3=A4user?= +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 ++#include ++#include ++#include ++#include ++ ++static RefPtr client; ++static OwnPtr event_loop; ++ ++static Array 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(); ++ 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 ++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) { ++ if constexpr (NumericLimits::is_signed()) { ++ // Signed integer samples are centered around zero, so this division is enough. ++ return static_cast(AK::convert_between_host_and_little_endian(sample)) / static_cast(NumericLimits::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(AK::convert_between_host_and_little_endian(sample)) / (static_cast(NumericLimits::max()) / 2.0) - 1.0; ++ } ++ } else { ++ return static_cast(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(b); ++ else ++ return read_sample(b); ++ } else { ++ if (format & XMP_FORMAT_8BIT) ++ return read_sample(b); ++ else ++ return read_sample(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 = + # Output device to be used + # +-#driver = alsa ++driver = serenity + + + # ALSA driver diff --git a/Ports/xmp-cli/patches/ReadMe.md b/Ports/xmp-cli/patches/ReadMe.md new file mode 100644 index 00000000000..fdb0f5994eb --- /dev/null +++ b/Ports/xmp-cli/patches/ReadMe.md @@ -0,0 +1,7 @@ +# Patches for xmp-cli on SerenityOS + +## `0001-Add-support-for-Serenity-LibAudio.patch` + +Add support for Serenity LibAudio + +