Hello,
The following patch is to make audio working on games/godot via libao. Six years ago, Anton Yabchinskiy added a libao driver[0] to have audio working on OpenBSD, but a year later[1] that code was removed. (n.b.: all of this was pre godot 2.0) I've resurrected that code and adapted so it compiles, but I am by no means an audio expert, so I'd like to have another pair of eyes in case it's missing something. Me and another person have been using this quite regularly since ~ April with success. To test this, you can download the demo projects from https://github.com/godotengine/godot-demo-projects and try, for example, 2d/dodge_the_creeps or 3d/platformer, i.e.: cd godot-demo-project/2d/dodge_the_creeps godot # the game should appear in its own window A final note regarding the demo projects: some of the projects in that repo fails to start (they trigger an assert -- not related to this patch). I haven't really looked into it, since just yesterday godot 3.2.3 was announced[2] and should fix some regression of godot 3.2.2, so I'll wait for that release first. Thoughts and comments welcome! [0]: https://github.com/godotengine/godot/pull/903 [1]: https://github.com/godotengine/godot/pull/2840 [2]: https://godotengine.org/article/dev-snapshot-godot-3-2-3-beta-1 Index: Makefile =================================================================== RCS file: /cvs/ports/games/godot/Makefile,v retrieving revision 1.11 diff -u -p -r1.11 Makefile --- Makefile 19 Jul 2020 13:02:38 -0000 1.11 +++ Makefile 21 Jul 2020 06:58:30 -0000 @@ -8,13 +8,14 @@ PKGNAME = godot-${V} CATEGORIES = games HOMEPAGE = https://godotengine.org/ MAINTAINER = Thomas Frohwein <t...@openbsd.org> +REVISION = 0 # MIT PERMIT_PACKAGE = Yes WANTLIB += ${COMPILER_LIBCXX} WANTLIB += GL X11 Xau Xcursor Xdmcp Xext Xfixes Xi Xinerama Xrandr -WANTLIB += Xrender c enet execinfo freetype intl m mbedtls mbedcrypto +WANTLIB += Xrender ao c enet execinfo freetype intl m mbedtls mbedcrypto WANTLIB += mbedx509 mpcdec ogg opus opusfile png theora theoradec WANTLIB += vorbis vorbisfile webp xcb z pcre2-32 vpx zstd @@ -54,6 +55,7 @@ MODSCONS_FLAGS = CC="${CC}" \ pulseaudio=no \ target=release_debug LIB_DEPENDS = archivers/zstd \ + audio/libao \ audio/libvorbis \ audio/musepack \ audio/opusfile \ @@ -80,6 +82,9 @@ CFLAGS += -mlongcall CXXFLAGS += -mlongcall LDFLAGS += -Wl,--relax .endif + +post-extract: + cp -R ${FILESDIR}/ao ${WRKDIST}/drivers pre-configure: ${SUBST_CMD} ${WRKSRC}/drivers/unix/os_unix.cpp Index: patches/patch-SConstruct =================================================================== RCS file: patches/patch-SConstruct diff -N patches/patch-SConstruct --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-SConstruct 21 Jul 2020 06:58:30 -0000 @@ -0,0 +1,15 @@ +$OpenBSD$ + +bring back libao. revert #903 + +Index: SConstruct +--- SConstruct.orig ++++ SConstruct +@@ -145,6 +145,7 @@ opts.Add("system_certs_path", "Use this path as SSL ce + + # Thirdparty libraries + # opts.Add(BoolVariable('builtin_assimp', "Use the built-in Assimp library", True)) ++opts.Add(BoolVariable("ao", "Use libao", True)) + opts.Add(BoolVariable("builtin_bullet", "Use the built-in Bullet library", True)) + opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundles", True)) + opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True)) Index: patches/patch-drivers_SCsub =================================================================== RCS file: patches/patch-drivers_SCsub diff -N patches/patch-drivers_SCsub --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-drivers_SCsub 21 Jul 2020 06:58:30 -0000 @@ -0,0 +1,15 @@ +$OpenBSD$ + +bring back libao. revert #903 + +Index: drivers/SCsub +--- drivers/SCsub.orig ++++ drivers/SCsub +@@ -10,6 +10,7 @@ SConscript("windows/SCsub") + + # Sounds drivers + SConscript("alsa/SCsub") ++SConscript("ao/SCsub") + SConscript("coreaudio/SCsub") + SConscript("pulseaudio/SCsub") + if env["platform"] == "windows": Index: patches/patch-platform_x11_detect_py =================================================================== RCS file: /cvs/ports/games/godot/patches/patch-platform_x11_detect_py,v retrieving revision 1.2 diff -u -p -r1.2 patch-platform_x11_detect_py --- patches/patch-platform_x11_detect_py 19 Jul 2020 13:02:38 -0000 1.2 +++ patches/patch-platform_x11_detect_py 21 Jul 2020 06:58:30 -0000 @@ -1,6 +1,7 @@ $OpenBSD: patch-platform_x11_detect_py,v 1.2 2020/07/19 13:02:38 thfr Exp $ -remove hardcoded -O2, found by bcallah@ + - remove hardcoded -O2, found by bcallah@ + - bring back libao. revert #903 Index: platform/x11/detect.py --- platform/x11/detect.py.orig @@ -27,3 +28,17 @@ Index: platform/x11/detect.py env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) if env["debug_symbols"] == "yes": +@@ -302,6 +293,13 @@ def configure(env): + env.ParseConfig("pkg-config alsa --libs") + else: + print("ALSA libraries not found, disabling driver") ++ ++ if os.system("pkg-config --exists ao") == 0: # 0 means found ++ print("Enabling ao") ++ env.Append(CPPDEFINES=["AO_ENABLED"]) ++ env.ParseConfig("pkg-config --cflags --libs ao") ++ else: ++ print("libao not found, disabling driver") + + if env["pulseaudio"]: + if os.system("pkg-config --exists libpulse") == 0: # 0 means found Index: patches/patch-platform_x11_os_x11_cpp =================================================================== RCS file: /cvs/ports/games/godot/patches/patch-platform_x11_os_x11_cpp,v retrieving revision 1.2 diff -u -p -r1.2 patch-platform_x11_os_x11_cpp --- patches/patch-platform_x11_os_x11_cpp 19 Jul 2020 13:02:38 -0000 1.2 +++ patches/patch-platform_x11_os_x11_cpp 21 Jul 2020 06:58:30 -0000 @@ -1,6 +1,7 @@ $OpenBSD: patch-platform_x11_os_x11_cpp,v 1.2 2020/07/19 13:02:38 thfr Exp $ -fix libXrandr library name + - fix libXrandr library name + - bring back libao. revert #903 Index: platform/x11/os_x11.cpp --- platform/x11/os_x11.cpp.orig @@ -18,3 +19,14 @@ Index: platform/x11/os_x11.cpp } else { XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor); if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) { +@@ -3596,6 +3596,10 @@ void OS_X11::update_real_mouse_position() { + } + + OS_X11::OS_X11() { ++ ++#ifdef AO_ENABLED ++ AudioDriverManager::add_driver(&driver_ao); ++#endif + + #ifdef PULSEAUDIO_ENABLED + AudioDriverManager::add_driver(&driver_pulseaudio); Index: patches/patch-platform_x11_os_x11_h =================================================================== RCS file: patches/patch-platform_x11_os_x11_h diff -N patches/patch-platform_x11_os_x11_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-platform_x11_os_x11_h 21 Jul 2020 06:58:30 -0000 @@ -0,0 +1,26 @@ +$OpenBSD$ + +bring back libao. revert #903 + +Index: platform/x11/os_x11.h +--- platform/x11/os_x11.h.orig ++++ platform/x11/os_x11.h +@@ -36,6 +36,7 @@ + #include "crash_handler_x11.h" + #include "drivers/alsa/audio_driver_alsa.h" + #include "drivers/alsamidi/midi_driver_alsamidi.h" ++#include "drivers/ao/audio_driver_ao.h" + #include "drivers/pulseaudio/audio_driver_pulseaudio.h" + #include "drivers/unix/os_unix.h" + #include "joypad_linux.h" +@@ -185,6 +186,10 @@ class OS_X11 : public OS_Unix { + + #ifdef ALSAMIDI_ENABLED + MIDIDriverALSAMidi driver_alsamidi; ++#endif ++ ++#ifdef AO_ENABLED ++ AudioDriverAO driver_ao; + #endif + + #ifdef PULSEAUDIO_ENABLED Index: files/ao/SCsub =================================================================== RCS file: files/ao/SCsub diff -N files/ao/SCsub --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ files/ao/SCsub 21 Jul 2020 06:42:59 -0000 @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# $OpenBSD$ + +Import('env') + +env.add_source_files(env.drivers_sources, "*.cpp") Index: files/ao/audio_driver_ao.cpp =================================================================== RCS file: files/ao/audio_driver_ao.cpp diff -N files/ao/audio_driver_ao.cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ files/ao/audio_driver_ao.cpp 21 Jul 2020 06:43:14 -0000 @@ -0,0 +1,153 @@ +/* $OpenBSD$ */ +/* + * revert of https://github.com/godotengine/godot/pull/2840 + * + * original code by Anton Yabchinskiy aka a12n on github, + * adapted to compile on newer versions of godot. + */ + +#include "audio_driver_ao.h" + +#ifdef AO_ENABLED + +#include "core/os/os.h" +#include "core/project_settings.h" + +#include <stdio.h> + +#include <cstring> + +unsigned int nearest_power_of_2(unsigned int v) { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + return v; +} + +Error AudioDriverAO::init() { + active = false; + thread_exited = false; + exit_thread = false; + pcm_open = false; + samples_in = NULL; + + mix_rate = 44100; + speaker_mode = SPEAKER_MODE_STEREO; + channels = 2; + + ao_sample_format format; + + format.bits = 16; + format.rate = mix_rate; + format.channels = channels; + format.byte_format = AO_FMT_LITTLE; + format.matrix = (char*)"L,R"; + + device = ao_open_live(ao_default_driver_id(), &format, NULL); + ERR_FAIL_COND_V(device == NULL, ERR_CANT_OPEN); + + int latency = GLOBAL_DEF("audio/output_latency", 25); + buffer_size = nearest_power_of_2(latency * mix_rate / 1000); + + samples_in = memnew_arr(int32_t, buffer_size * channels); + + mutex = Mutex::create(); + thread = Thread::create(AudioDriverAO::thread_func, this); + + return OK; +} + +void AudioDriverAO::thread_func(void *p_udata) { + AudioDriverAO *ad = (AudioDriverAO*)p_udata; + + // overwrite samples on conversion + int16_t *samples_out = reinterpret_cast<int16_t*>(ad->samples_in); + unsigned int n_samples = ad->buffer_size * ad->channels; + + // why sizeof? isn't it 2 by definition? + unsigned int n_bytes = n_samples * sizeof(int16_t); + + while (!ad->exit_thread) { + if (ad->active) { + ad->lock(); + ad->audio_server_process(ad->buffer_size, ad->samples_in); + ad->unlock(); + + for (unsigned int i = 0; i < n_samples; i++) { + samples_out[i] = ad->samples_in[i] >> 16; + } + } else { + memset(samples_out, 0, n_bytes); + } + + if (ad->exit_thread) + break; + + if (!ao_play(ad->device, reinterpret_cast<char*>(samples_out), n_bytes)) { + ERR_PRINT("ao_play() failed"); + } + } + + ad->thread_exited = true; +} + +void AudioDriverAO::start() { + active = true; +} + +int AudioDriverAO::get_mix_rate() const { + return mix_rate; +} + +AudioDriver::SpeakerMode AudioDriverAO::get_speaker_mode() const { + return speaker_mode; +} + +void AudioDriverAO::lock() { + if (!thread || !mutex) + return; + mutex->lock(); +} + +void AudioDriverAO::unlock() { + if (!thread || !mutex) + return; + mutex->unlock(); +} + +void AudioDriverAO::finish() { + if (!thread) + return; + + exit_thread = true; + Thread::wait_to_finish(thread); + + if (samples_in) + memdelete_arr(samples_in); + + memdelete(thread); + if (mutex) + memdelete(mutex); + if (device) + ao_close(device); + + thread = NULL; +} + +AudioDriverAO::AudioDriverAO() { + mutex = NULL; + thread = NULL; + + ao_initialize(); +} + +AudioDriverAO::~AudioDriverAO() { + ao_shutdown(); +} + +#endif Index: files/ao/audio_driver_ao.h =================================================================== RCS file: files/ao/audio_driver_ao.h diff -N files/ao/audio_driver_ao.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ files/ao/audio_driver_ao.h 21 Jul 2020 06:43:20 -0000 @@ -0,0 +1,53 @@ +/* $OpenBSD$ */ +/* + * revert of https://github.com/godotengine/godot/pull/2840 + * + * original code by Anton Yabchinskiy aka a12n on github, + * adapted to compile on newer versions of godot. + */ + +#include "servers/audio_server.h" + +#ifdef AO_ENABLED + +#include "core/os/mutex.h" +#include "core/os/thread.h" + +#include <ao/ao.h> + +class AudioDriverAO : public AudioDriver { + Thread *thread; + Mutex *mutex; + + ao_device *device; + int32_t *samples_in; + + static void thread_func(void*); + int buffer_size; + + unsigned int mix_rate; + int channels; + bool active; + bool thread_exited; + mutable bool exit_thread; + bool pcm_open; + SpeakerMode speaker_mode; + +public: + const char *get_name() const { + return "libao"; + } + + virtual Error init(); + virtual void start(); + virtual int get_mix_rate() const; + virtual SpeakerMode get_speaker_mode() const; + virtual void lock(); + virtual void unlock(); + virtual void finish(); + + AudioDriverAO(); + ~AudioDriverAO(); +}; + +#endif