From e8e1c83dd8c8593921201152278fa7d9ab093ba4 Mon Sep 17 00:00:00 2001
From: Julian Harnath <julian.harnath@rwth-aachen.de>
Date: Mon, 24 Mar 2014 21:30:27 +0100
Subject: Add Haiku Media Kit audio output driver.


diff --git a/configure.ac b/configure.ac
index b5aba33..eb60271 100644
--- a/configure.ac
+++ b/configure.ac
@@ -30,13 +30,15 @@ dnl Check for programs
 dnl ====================================
 
 cflags_save="$CFLAGS"
+cppflags_save="$CPPFLAGS"
 AC_PROG_CC
+AC_PROG_CXX
 AC_LIBTOOL_WIN32_DLL
 AC_LIBTOOL_DLOPEN
 AM_PROG_LIBTOOL
 
 dnl ====================================
-dnl Check dlopen 
+dnl Check dlopen
 dnl ====================================
 
 dnl Currently use this to disable plugin support dlfcn.h
@@ -68,6 +70,7 @@ if test -z "$GCC"; then
                 PLUGIN_LDFLAGS="-export-dynamic -avoid-version"
                 DEBUG="-g"
                 CFLAGS="-O"
+                CPPFLAGS="-O"
                 PROFILE="-g -p" ;;
         esac
 else
@@ -92,10 +95,12 @@ else
                 PLUGIN_LDFLAGS="-export-dynamic -avoid-version"
                 DEBUG="-g -Wall -D__NO_MATH_INLINES -fsigned-char"
                 CFLAGS="-O20 -D__NO_MATH_INLINES -fsigned-char"
+                CPPFLAGS="-O20 -D__NO_MATH_INLINES -fsigned-char"
                 PROFILE="-O20 -g -pg -D__NO_MATH_INLINES -fsigned-char" ;;
         esac
 fi
 CFLAGS="$CFLAGS $cflags_save -DAO_BUILDING_LIBAO"
+CPPFLAGS="$CPPFLAGS $cppflags_save -DAO_BUILDING_LIBAO"
 DEBUG="$DEBUG $cflags_save -DAO_BUILDING_LIBAO"
 PROFILE="$PROFILE $cflags_save -DAO_BUILDING_LIBAO"
 
@@ -111,7 +116,7 @@ case $host in
     *-mingw*|*-cygwin*)
 	LIBAO_LA_LDFLAGS="-no-undefined"
 	;;
-    *)	
+    *)
 	LIBAO_LA_LDFLAGS=""
 	;;
 esac
@@ -151,7 +156,7 @@ case $host in
     *hpux*)
 	DLOPEN_FLAG='(RTLD_LAZY)'
 	SHARED_LIB_EXT='.sl'
-	;;    
+	;;
     *openbsd* | *netbsd* | *solaris2.7 | *darwin*)
 	DLOPEN_FLAG='(RTLD_LAZY)'
 	SHARED_LIB_EXT='.so'
@@ -194,7 +199,7 @@ AC_SUBST(SIZE16)
 AC_SUBST(SIZE32)
 
 dnl ======================================
-dnl Disable default use of SLP in roar 
+dnl Disable default use of SLP in roar
 dnl until such time as SLP doesn't block
 dnl indefinitely
 dnl ======================================
@@ -212,7 +217,7 @@ fi
 AC_SUBST([SLP_DEF])
 
 dnl ======================================
-dnl Detect possible output devices 
+dnl Detect possible output devices
 dnl ======================================
 
 dnl Check for WMM
@@ -309,7 +314,7 @@ if test "x$enable_broken_oss" = "xyes"; then
    AC_DEFINE(BROKEN_OSS)
    AC_MSG_WARN(Broken OSS API workaround enabled.  See README for details.)
 fi
-   
+
 dnl Check for Sun audio
 
 have_sun="no";
@@ -424,7 +429,7 @@ if test "$BUILD_NAS" = "yes"; then
    AC_PATH_XTRA
    AC_CHECK_LIB(Xau, XauFileName, have_nas=yes, have_nas=no, $X_LIBS)
    AC_CHECK_LIB(audio, AuOpenServer, dummy="no-op", have_nas=no, -lXau $X_LIBS)
-   
+
    ac_save_CPPFLAGS="$CPPFLAGS"
    CPPFLAGS="$CPPFLAGS $X_CFLAGS"
    AC_CHECK_HEADER(audio/audiolib.h, dummy="no-op", have_nas=no)
@@ -458,6 +463,12 @@ fi
 
 AM_CONDITIONAL(HAVE_PULSE,test "x$have_pulse" = xyes)
 
+dnl Check for Haiku audio
+
+have_haiku="no";
+AC_CHECK_HEADERS(MediaDefs.h, have_haiku=yes)
+AM_CONDITIONAL(HAVE_HAIKU,test "${have_haiku}" = yes)
+
 dnl Orphaned driver.  We'll probably dump it soon.
 AM_CONDITIONAL(HAVE_SOLARIS,test "x$have_solaris" = xyes)
 
@@ -465,7 +476,7 @@ dnl Plugins get special LDFLAGS
 AC_SUBST(PLUGIN_LDFLAGS)
 
 
-AC_OUTPUT([Makefile src/Makefile doc/Makefile include/Makefile include/ao/Makefile include/ao/os_types.h src/plugins/Makefile src/plugins/esd/Makefile src/plugins/oss/Makefile src/plugins/alsa/Makefile src/plugins/sun/Makefile src/plugins/irix/Makefile src/plugins/arts/Makefile src/plugins/macosx/Makefile src/plugins/nas/Makefile src/plugins/pulse/Makefile src/plugins/sndio/Makefile src/plugins/roar/Makefile ao.pc])
+AC_OUTPUT([Makefile src/Makefile doc/Makefile include/Makefile include/ao/Makefile include/ao/os_types.h src/plugins/Makefile src/plugins/esd/Makefile src/plugins/oss/Makefile src/plugins/alsa/Makefile src/plugins/sun/Makefile src/plugins/irix/Makefile src/plugins/arts/Makefile src/plugins/macosx/Makefile src/plugins/nas/Makefile src/plugins/pulse/Makefile src/plugins/sndio/Makefile src/plugins/roar/Makefile src/plugins/haiku/Makefile ao.pc])
 
 AS_AC_EXPAND(LIBDIR, ${libdir})
 AS_AC_EXPAND(INCLUDEDIR, ${includedir})
@@ -486,6 +497,7 @@ AC_MSG_RESULT([
     ALSA live output: ............ ${have_alsa}
     ARTS live output: ............ ${have_arts}
     ESD live output: ............. ${have_esd}
+    HAIKU live output: ........... ${have_haiku}
     IRIX live output: ............ ${have_irix}
     MACOSX live output: .......... ${have_macosx}
     NAS live output: ............. ${have_nas}
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 1af2303..1b417d4 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -1,4 +1,4 @@
 ## Process this file with automake to produce Makefile.in
 
 AUTOMAKE_OPTIONS = foreign
-SUBDIRS = oss esd arts alsa sun irix macosx nas pulse sndio roar
+SUBDIRS = oss esd arts alsa sun irix macosx nas pulse sndio roar haiku
diff --git a/src/plugins/haiku/Makefile.am b/src/plugins/haiku/Makefile.am
new file mode 100644
index 0000000..596c664
--- /dev/null
+++ b/src/plugins/haiku/Makefile.am
@@ -0,0 +1,25 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+if HAVE_HAIKU
+
+haikultlibs = libhaiku.la
+haikusources = ao_haiku.cpp
+
+else
+
+haikultlibs =
+haikusources =
+
+endif
+
+INCLUDES = -I$(top_builddir)/include/ao -I$(top_srcdir)/include
+
+libdir = $(plugindir)
+lib_LTLIBRARIES = $(haikultlibs)
+
+libhaiku_la_LDFLAGS = @PLUGIN_LDFLAGS@ -lbe -lmedia
+libhaiku_la_SOURCES = $(haikusources)
+
+EXTRA_DIST = ao_haiku.cpp
diff --git a/src/plugins/haiku/ao_haiku.cpp b/src/plugins/haiku/ao_haiku.cpp
new file mode 100644
index 0000000..58d9dcb
--- /dev/null
+++ b/src/plugins/haiku/ao_haiku.cpp
@@ -0,0 +1,254 @@
+/*
+ *  ao_haiku.cpp
+ *
+ *      Copyright (C) Julian Harnath - March 2014
+ *
+ *  This file is part of libao, a cross-platform audio output library.  See
+ *  README for a history of this source code.
+ *
+ *  libao is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  libao is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ********************************************************************/
+
+#include <algorithm>
+#include <string.h>
+#include <stdio.h>
+
+#include <MediaDefs.h>
+#include <MediaRoster.h>
+#include <OS.h>
+#include <SoundPlayer.h>
+
+#include <ao/ao.h>
+#include <ao/plugin.h>
+
+
+static char* ao_haiku_options[] = {
+	"matrix"
+};
+
+
+struct ao_info ao_haiku_info = {
+	AO_TYPE_LIVE,
+	"Haiku Media Kit Output",
+	"haiku",
+	"Julian Harnath <julian.harnath@rwth-aachen.de>",
+	"Outputs to the Haiku Media Kit",
+	AO_FMT_NATIVE,
+	30,
+	ao_haiku_options,
+	sizeof(ao_haiku_options) / sizeof(*ao_haiku_options)
+};
+
+
+typedef struct ao_haiku_internal {
+	media_raw_audio_format* format;
+	BSoundPlayer* sound_player;
+
+	sem_id new_buffer;
+	sem_id buffer_done;
+
+	uint8* buffer;
+	size_t buffer_size;
+	size_t buffer_filled;
+} ao_haiku_internal;
+
+
+int
+ao_plugin_test()
+{
+	BSoundPlayer testPlayer;
+	return testPlayer.InitCheck() == B_OK ? 1 :0;
+}
+
+
+ao_info*
+ao_plugin_driver_info()
+{
+	return &ao_haiku_info;
+}
+
+
+int
+ao_plugin_device_init(ao_device* device)
+{
+	ao_haiku_internal* const internal = (ao_haiku_internal*)calloc(1,
+		sizeof(ao_haiku_internal));
+	if (internal == NULL)
+		return 0;
+
+	internal->format = (media_raw_audio_format*)malloc(
+		sizeof(media_raw_audio_format));
+	if (internal->format == NULL) {
+		free(internal);
+		return 0;
+	}
+
+	internal->new_buffer = create_sem(0, "New buffer request");
+	internal->buffer_done = create_sem(1, "Buffer done");
+
+	device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
+	device->internal = internal;
+
+	return 1;
+}
+
+
+int
+ao_plugin_set_option(ao_device* device, const char* key, const char* value)
+{
+	return 1;
+}
+
+
+static void
+fill_buffer(void* cookie, void* buffer, size_t size,
+	const media_raw_audio_format& format)
+{
+	ao_haiku_internal* const internal = (ao_haiku_internal*)cookie;
+
+	internal->buffer = (uint8*)buffer;
+	internal->buffer_size = size;
+	internal->buffer_filled = 0;
+	release_sem(internal->new_buffer);
+	acquire_sem(internal->buffer_done);
+}
+
+
+int
+ao_plugin_open(ao_device* device, ao_sample_format* format)
+{
+	ao_haiku_internal* const internal = (ao_haiku_internal*)device->internal;
+	media_raw_audio_format* const mediaRawFormat = internal->format;
+
+	mediaRawFormat->frame_rate = format->rate;
+	mediaRawFormat->channel_count = device->output_channels;
+
+	switch (format->bits) {
+		case 8:
+			mediaRawFormat->format = media_raw_audio_format::B_AUDIO_CHAR;
+			break;
+		case 16:
+			mediaRawFormat->format = media_raw_audio_format::B_AUDIO_SHORT;
+			break;
+		case 32:
+			mediaRawFormat->format = media_raw_audio_format::B_AUDIO_INT;
+			break;
+		default:
+			aerror("Unsupported sample bit depth");
+			return 0;
+	}
+
+	device->driver_byte_format = AO_FMT_NATIVE;
+	if (B_HOST_IS_LENDIAN)
+		mediaRawFormat->byte_order = B_MEDIA_LITTLE_ENDIAN;
+	else
+		mediaRawFormat->byte_order = B_MEDIA_BIG_ENDIAN;
+
+	mediaRawFormat->buffer_size = BMediaRoster::Roster()->AudioBufferSizeFor(
+		mediaRawFormat->channel_count, mediaRawFormat->format,
+		mediaRawFormat->frame_rate, B_UNKNOWN_BUS) * 2;
+
+	internal->sound_player = new BSoundPlayer(mediaRawFormat, "ao player",
+		fill_buffer, NULL, internal);
+
+	if (internal->sound_player->InitCheck() != B_OK) {
+		delete internal->sound_player;
+		internal->sound_player = NULL;
+		return 0;
+	}
+
+	internal->sound_player->SetVolume(1.0);
+	internal->sound_player->Start();
+	internal->sound_player->SetHasData(false);
+
+	if (device->inter_matrix == NULL) {
+		if (device->output_channels <= 2)
+			device->inter_matrix = strdup("L,R");
+	}
+
+	return 1;
+}
+
+
+int
+ao_plugin_play(ao_device* device, const char* output_samples,
+	uint_32 num_bytes)
+{
+	ao_haiku_internal* const internal = (ao_haiku_internal*)device->internal;
+	BSoundPlayer* const soundPlayer = internal->sound_player;
+
+	if (num_bytes == 0) {
+		soundPlayer->SetHasData(false);
+		return 1;
+	}
+
+	soundPlayer->SetHasData(true);
+	acquire_sem(internal->new_buffer);
+
+	size_t bytesLeft = num_bytes;
+	while (bytesLeft > 0) {
+		if (internal->buffer_filled == internal->buffer_size) {
+			// Request another buffer from BSoundPlayer
+			release_sem(internal->buffer_done);
+			acquire_sem(internal->new_buffer);
+		}
+
+		const size_t copyBytes = std::min(bytesLeft, internal->buffer_size
+			- internal->buffer_filled);
+		memcpy(internal->buffer + internal->buffer_filled, output_samples,
+			copyBytes);
+		internal->buffer_filled += copyBytes;
+		output_samples += copyBytes;
+		bytesLeft -= copyBytes;
+	}
+
+	if (internal->buffer_filled < internal->buffer_size) {
+		// Continue filling this buffer the next time this function is called
+		release_sem(internal->new_buffer);
+	} else {
+		// Buffer is full
+		release_sem(internal->buffer_done);
+		soundPlayer->SetHasData(false);
+	}
+
+	return 1;
+}
+
+
+int
+ao_plugin_close(ao_device* device)
+{
+	ao_haiku_internal* const internal = (ao_haiku_internal*)device->internal;
+
+	release_sem(internal->buffer_done);
+	internal->sound_player->SetHasData(false);
+	internal->sound_player->Stop();
+	delete internal->sound_player;
+
+	return 1;
+}
+
+
+void
+ao_plugin_device_clear(ao_device* device)
+{
+	ao_haiku_internal* const internal = (ao_haiku_internal*)device->internal;
+
+	free(internal->format);
+	delete_sem(internal->new_buffer);
+	delete_sem(internal->buffer_done);
+	free(internal);
+}
-- 
1.8.3.4

