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