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

Reply via email to