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 <[email protected]>
+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