changeset 3761:2dc218267f9c

. Sound support for FreeBSD using the ALSA compatibility library. It looks like NetBSD also has an ALSA compatibility library, but I don't have access to a NetBSD system to test. I don't see any support for ALSA on OpenBSD or MacOS X. Ideally this will eventually be replaced by a version that uses the native sound infrastructure, although doing it this way certainly reduces the maintenance cost. Discussed with Jung-uk Kim who did a similar change for FreeBSD's openjdk6 port, although I didn't end up looking at that (the changes are simple enough that I expect they are similar though).
author Greg Lewis <glewis@eyesbeyond.com>
date Sun, 03 Apr 2011 10:33:53 -0700
parents 213d508b85cc
children 241d5769859e
files make/common/Defs.gmk make/javax/sound/Makefile make/javax/sound/jsoundalsa/Makefile src/share/native/com/sun/media/sound/Platform.c src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_CommonUtils.c src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_CommonUtils.h src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_MidiIn.c src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_MidiOut.c src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_MidiUtils.c src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_MidiUtils.h src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_PCM.c src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_PCMUtils.c src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_PCMUtils.h src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_Ports.c
diffstat 14 files changed, 3389 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/make/common/Defs.gmk	Sun Mar 20 11:05:38 2011 -0700
+++ b/make/common/Defs.gmk	Sun Apr 03 10:33:53 2011 -0700
@@ -137,6 +137,19 @@
       endif
     endif
   endif
+
+  # ALSA
+  ifdef ALT_ALSA_LIB_PATH
+    ALSA_LIB_PATH = $(ALT_ALSA_LIB_PATH)
+  else
+    ALSA_LIB_PATH = $(PACKAGE_PATH)/lib
+  endif
+
+  ifdef ALT_ALSA_HEADERS_PATH
+    ALSA_HEADERS_PATH = $(ALT_ALSA_HEADERS_PATH)
+  else
+    ALSA_HEADERS_PATH = $(PACKAGE_PATH)/include
+  endif
 endif
 
 #
--- a/make/javax/sound/Makefile	Sun Mar 20 11:05:38 2011 -0700
+++ b/make/javax/sound/Makefile	Sun Apr 03 10:33:53 2011 -0700
@@ -108,12 +108,18 @@
 
 # XXXBSD: ???
 ifeq ($(PLATFORM), bsd)
+ifeq ($(OS_VENDOR), FreeBSD)
+  # ALSA handles directaudio, ports, and MIDI
+  SUBDIRS += jsoundalsa
+  EXTRA_SOUND_JNI_LIBS += jsoundalsa
+else
     # build with empty MIDI i/o
     INCLUDE_MIDI = TRUE
     # build with empty ports
     INCLUDE_PORTS = TRUE
     # build with empty direct audio
     INCLUDE_DAUDIO = TRUE
+endif
 endif # PLATFORM bsd
 
 ifeq ($(PLATFORM), solaris)
--- a/make/javax/sound/jsoundalsa/Makefile	Sun Mar 20 11:05:38 2011 -0700
+++ b/make/javax/sound/jsoundalsa/Makefile	Sun Apr 03 10:33:53 2011 -0700
@@ -52,6 +52,7 @@
 	$(PORTFILES_c)
 
 # platform dependent files
+ifeq ($(PLATFORM), linux)
 FILES_c += \
 	PLATFORM_API_LinuxOS_ALSA_CommonUtils.c   \
 	PLATFORM_API_LinuxOS_ALSA_PCM.c     \
@@ -60,20 +61,33 @@
 	PLATFORM_API_LinuxOS_ALSA_MidiOut.c \
 	PLATFORM_API_LinuxOS_ALSA_MidiUtils.c \
 	PLATFORM_API_LinuxOS_ALSA_Ports.c
+endif
+
+ifeq ($(PLATFORM), bsd)
+FILES_c += \
+	PLATFORM_API_BsdOS_ALSA_CommonUtils.c   \
+	PLATFORM_API_BsdOS_ALSA_PCM.c     \
+	PLATFORM_API_BsdOS_ALSA_PCMUtils.c   \
+	PLATFORM_API_BsdOS_ALSA_MidiIn.c  \
+	PLATFORM_API_BsdOS_ALSA_MidiOut.c \
+	PLATFORM_API_BsdOS_ALSA_MidiUtils.c \
+	PLATFORM_API_BsdOS_ALSA_Ports.c
+endif
 
 FILES_export = \
 	$(DAUDIOFILES_export) \
 	$(MIDIFILES_export) \
 	$(PORTFILES_export)
 
-LDFLAGS += -lasound
+LDFLAGS += -L$(ALSA_LIB_PATH) -lasound
 
 CPPFLAGS += \
 	-DUSE_DAUDIO=TRUE \
 	-DUSE_PORTS=TRUE  \
 	-DUSE_PLATFORM_MIDI_OUT=TRUE \
 	-DUSE_PLATFORM_MIDI_IN=TRUE \
-	-I$(SHARE_SRC)/native/com/sun/media/sound
+	-I$(SHARE_SRC)/native/com/sun/media/sound \
+	-I$(ALSA_HEADERS_PATH)
 
 #
 # Add to the ambient VPATH.
--- a/src/share/native/com/sun/media/sound/Platform.c	Sun Mar 20 11:05:38 2011 -0700
+++ b/src/share/native/com/sun/media/sound/Platform.c	Sun Apr 03 10:33:53 2011 -0700
@@ -101,6 +101,14 @@
 #endif
 #if (X_PLATFORM == X_BSD)
     switch (feature) {
+#ifdef __FreeBSD__
+    case com_sun_media_sound_Platform_FEATURE_MIDIIO:
+       return com_sun_media_sound_Platform_LIB_ALSA;
+    case com_sun_media_sound_Platform_FEATURE_PORTS:
+       return com_sun_media_sound_Platform_LIB_ALSA;
+    case com_sun_media_sound_Platform_FEATURE_DIRECT_AUDIO:
+       return com_sun_media_sound_Platform_LIB_ALSA;
+#else
     case com_sun_media_sound_Platform_FEATURE_MIDIIO:
        return com_sun_media_sound_Platform_LIB_MAIN;
     case com_sun_media_sound_Platform_FEATURE_PORTS:
@@ -109,6 +117,7 @@
        // XXXBSD: When native Direct Audio support is ported change
        // this back to returning com_sun_media_sound_Platform_LIB_MAIN
        return 0;
+#endif
     }
 #endif
     return 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_CommonUtils.c	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+//#define USE_ERROR
+//#define USE_TRACE
+
+#include "PLATFORM_API_BsdOS_ALSA_CommonUtils.h"
+
+static void alsaDebugOutput(const char *file, int line, const char *function, int err, const char *fmt, ...) {
+#ifdef USE_ERROR
+    va_list args;
+    va_start(args, fmt);
+    printf("%s:%d function %s: error %d: %s\n", file, line, function, err, snd_strerror(err));
+    if (strlen(fmt) > 0) {
+        vprintf(fmt, args);
+    }
+    va_end(args);
+#endif
+}
+
+static int alsa_inited = 0;
+static int alsa_enumerate_pcm_subdevices = FALSE; // default: no
+static int alsa_enumerate_midi_subdevices = FALSE; // default: no
+
+void initAlsaSupport() {
+    char* enumerate;
+    if (!alsa_inited) {
+        alsa_inited = TRUE;
+        snd_lib_error_set_handler(&alsaDebugOutput);
+
+        enumerate = getenv(ENV_ENUMERATE_PCM_SUBDEVICES);
+        if (enumerate != NULL && strlen(enumerate) > 0
+            && (enumerate[0] != 'f')   // false
+            && (enumerate[0] != 'F')   // False
+            && (enumerate[0] != 'n')   // no
+            && (enumerate[0] != 'N')) { // NO
+            alsa_enumerate_pcm_subdevices = TRUE;
+        }
+#ifdef ALSA_MIDI_ENUMERATE_SUBDEVICES
+        alsa_enumerate_midi_subdevices = TRUE;
+#endif
+    }
+}
+
+
+/* if true (non-zero), ALSA sub devices should be listed as separate devices
+ */
+int needEnumerateSubdevices(int isMidi) {
+    initAlsaSupport();
+    return isMidi ? alsa_enumerate_midi_subdevices
+                  : alsa_enumerate_pcm_subdevices;
+}
+
+
+/*
+ * deviceID contains packed card, device and subdevice numbers
+ * each number takes 10 bits
+ * "default" device has id == ALSA_DEFAULT_DEVICE_ID
+ */
+UINT32 encodeDeviceID(int card, int device, int subdevice) {
+    return (((card & 0x3FF) << 20) | ((device & 0x3FF) << 10)
+           | (subdevice & 0x3FF)) + 1;
+}
+
+
+void decodeDeviceID(UINT32 deviceID, int* card, int* device, int* subdevice,
+                    int isMidi) {
+    deviceID--;
+    *card = (deviceID >> 20) & 0x3FF;
+    *device = (deviceID >> 10) & 0x3FF;
+    if (needEnumerateSubdevices(isMidi)) {
+        *subdevice = deviceID  & 0x3FF;
+    } else {
+        *subdevice = -1; // ALSA will choose any subdevices
+    }
+}
+
+
+void getDeviceString(char* buffer, int card, int device, int subdevice,
+                     int usePlugHw, int isMidi) {
+    if (needEnumerateSubdevices(isMidi)) {
+        sprintf(buffer, "%s:%d,%d,%d",
+                        usePlugHw ? ALSA_PLUGHARDWARE : ALSA_HARDWARE,
+                        card, device, subdevice);
+    } else {
+        sprintf(buffer, "%s:%d,%d",
+                        usePlugHw ? ALSA_PLUGHARDWARE : ALSA_HARDWARE,
+                        card, device);
+    }
+}
+
+
+void getDeviceStringFromDeviceID(char* buffer, UINT32 deviceID,
+                                 int usePlugHw, int isMidi) {
+    int card, device, subdevice;
+
+    if (deviceID == ALSA_DEFAULT_DEVICE_ID) {
+        strcpy(buffer, ALSA_DEFAULT_DEVICE_NAME);
+    } else {
+        decodeDeviceID(deviceID, &card, &device, &subdevice, isMidi);
+        getDeviceString(buffer, card, device, subdevice, usePlugHw, isMidi);
+    }
+}
+
+
+static int hasGottenALSAVersion = FALSE;
+#define ALSAVersionString_LENGTH 200
+static char ALSAVersionString[ALSAVersionString_LENGTH];
+
+void getALSAVersion(char* buffer, int len) {
+    if (!hasGottenALSAVersion) {
+        // get alsa version from proc interface
+        FILE* file;
+        int curr, len, totalLen, inVersionString;
+        file = fopen(ALSA_VERSION_PROC_FILE, "r");
+        ALSAVersionString[0] = 0;
+        if (file) {
+            if (NULL != fgets(ALSAVersionString, ALSAVersionString_LENGTH, file)) {
+                // parse for version number
+                totalLen = strlen(ALSAVersionString);
+                inVersionString = FALSE;
+                len = 0;
+                curr = 0;
+                while (curr < totalLen) {
+                    if (!inVersionString) {
+                        // is this char the beginning of a version string ?
+                        if (ALSAVersionString[curr] >= '0'
+                            && ALSAVersionString[curr] <= '9') {
+                            inVersionString = TRUE;
+                        }
+                    }
+                    if (inVersionString) {
+                        // the version string ends with white space
+                        if (ALSAVersionString[curr] <= 32) {
+                            break;
+                        }
+                        if (curr != len) {
+                            // copy this char to the beginning of the string
+                            ALSAVersionString[len] = ALSAVersionString[curr];
+                        }
+                        len++;
+                    }
+                    curr++;
+                }
+                // remove trailing dots
+                while ((len > 0) && (ALSAVersionString[len - 1] == '.')) {
+                    len--;
+                }
+                // null terminate
+                ALSAVersionString[len] = 0;
+            }
+            fclose(file);
+            hasGottenALSAVersion = TRUE;
+        }
+    }
+    strncpy(buffer, ALSAVersionString, len);
+}
+
+
+/* end */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_CommonUtils.h	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <alsa/asoundlib.h>
+#include "Utilities.h"
+
+#ifndef PLATFORM_API_BSDOS_ALSA_COMMONUTILS_H_INCLUDED
+#define PLATFORM_API_BSDOS_ALSA_COMMONUTILS_H_INCLUDED
+
+#define ALSA_VERSION_PROC_FILE "/proc/asound/version"
+#define ALSA_HARDWARE "hw"
+#define ALSA_HARDWARE_CARD ALSA_HARDWARE":%d"
+#define ALSA_HARDWARE_DEVICE ALSA_HARDWARE_CARD",%d"
+#define ALSA_HARDWARE_SUBDEVICE ALSA_HARDWARE_DEVICE",%d"
+
+#define ALSA_PLUGHARDWARE "plughw"
+#define ALSA_DEFAULT_DEVICE_NAME "default"
+
+#define ALSA_DEFAULT_DEVICE_ID (0)
+
+#define ALSA_PCM     (0)
+#define ALSA_RAWMIDI (1)
+
+// for use in info objects
+#define ALSA_VENDOR "ALSA (http://www.alsa-project.org)"
+
+// Environment variable for inclusion of subdevices in device listing.
+// If this variable is unset or "no", then subdevices are ignored, and
+// it's ALSA's choice which one to use (enables hardware mixing)
+#define ENV_ENUMERATE_PCM_SUBDEVICES "ALSA_ENUMERATE_PCM_SUBDEVICES"
+
+// if defined, subdevices are listed.
+//#undef ALSA_MIDI_ENUMERATE_SUBDEVICES
+#define ALSA_MIDI_ENUMERATE_SUBDEVICES
+
+// must be called before any ALSA calls
+void initAlsaSupport();
+
+/* if true (non-zero), ALSA sub devices should be listed as separate devices
+ */
+int needEnumerateSubdevices(int isMidi);
+
+
+/*
+ * deviceID contains packed card, device and subdevice numbers
+ * each number takes 10 bits
+ * "default" device has id == ALSA_DEFAULT_DEVICE_ID
+ */
+UINT32 encodeDeviceID(int card, int device, int subdevice);
+
+void decodeDeviceID(UINT32 deviceID, int* card, int* device, int* subdevice,
+                    int isMidi);
+
+void getDeviceStringFromDeviceID(char* buffer, UINT32 deviceID,
+                                 int usePlugHw, int isMidi);
+
+void getALSAVersion(char* buffer, int len);
+
+
+#endif // PLATFORM_API_BSDOS_ALSA_COMMONUTILS_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_MidiIn.c	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#define USE_ERROR
+#define USE_TRACE
+
+#if USE_PLATFORM_MIDI_IN == TRUE
+
+
+#include <alsa/asoundlib.h>
+#include "PlatformMidi.h"
+#include "PLATFORM_API_BsdOS_ALSA_MidiUtils.h"
+#if defined(i586)
+#include <sys/utsname.h>
+#endif
+
+/*
+ * Helper methods
+ */
+
+static inline UINT32 packMessage(int status, int data1, int data2) {
+    return ((status & 0xFF) | ((data1 & 0xFF) << 8) | ((data2 & 0xFF) << 16));
+}
+
+
+static void setShortMessage(MidiMessage* message,
+                            int status, int data1, int data2) {
+    message->type = SHORT_MESSAGE;
+    message->data.s.packedMsg = packMessage(status, data1, data2);
+}
+
+
+static void setRealtimeMessage(MidiMessage* message, int status) {
+    setShortMessage(message, status, 0, 0);
+}
+
+
+static void set14bitMessage(MidiMessage* message, int status, int value) {
+    TRACE3("14bit value: %d, lsb: %d, msb: %d\n", value, value & 0x7F, (value >> 7) & 0x7F);
+    value &= 0x3FFF;
+    TRACE3("14bit value (2): %d, lsb: %d, msb: %d\n", value, value & 0x7F, (value >> 7) & 0x7F);
+    setShortMessage(message, status,
+                    value & 0x7F,
+                    (value >> 7) & 0x7F);
+}
+
+
+/*
+ * implementation of the platform-dependent
+ * MIDI in functions declared in PlatformMidi.h
+ */
+
+char* MIDI_IN_GetErrorStr(INT32 err) {
+    return (char*) getErrorStr(err);
+}
+
+INT32 MIDI_IN_GetNumDevices() {
+/* Workaround for 6842956: 32bit app on 64bit bsd
+ * gets assertion failure trying to open midiIn ports.
+ * Untill the issue is fixed in ALSA
+ * (https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4807)
+ * report no midi in devices in the configuration.
+ */
+#if defined(i586)
+    static int jre32onbsd64 = -1;
+    if (jre32onbsd64 < 0) {
+        jre32onbsd64 = 0;
+        /* The workaround may be disabled setting "JAVASOUND_ENABLE_MIDIIN"
+         * environment variable.
+         */
+        if (getenv("JAVASOUND_ENABLE_MIDIIN") == NULL) {
+            struct utsname u;
+            jre32onbsd64 = 0;
+            if (uname(&u) == 0) {
+                if (strstr(u.machine, "64") != NULL) {
+                    TRACE0("jre32 on bsd64 detected - report no midiIn devices\n");
+                    jre32onbsd64 = 1;
+                }
+            }
+        }
+    }
+    if (jre32onbsd64) {
+        return 0;
+    }
+#endif
+
+    TRACE0("MIDI_IN_GetNumDevices()\n");
+
+    return getMidiDeviceCount(SND_RAWMIDI_STREAM_INPUT);
+}
+
+
+INT32 MIDI_IN_GetDeviceName(INT32 deviceIndex, char *name, UINT32 nameLength) {
+    int ret = getMidiDeviceName(SND_RAWMIDI_STREAM_INPUT, deviceIndex,
+                                name, nameLength);
+    return ret;
+}
+
+
+INT32 MIDI_IN_GetDeviceVendor(INT32 deviceIndex, char *name, UINT32 nameLength) {
+    int ret = getMidiDeviceVendor(deviceIndex, name, nameLength);
+    return ret;
+}
+
+
+INT32 MIDI_IN_GetDeviceDescription(INT32 deviceIndex, char *name, UINT32 nameLength) {
+    int ret = getMidiDeviceDescription(SND_RAWMIDI_STREAM_INPUT, deviceIndex,
+                                       name, nameLength);
+    return ret;
+}
+
+
+INT32 MIDI_IN_GetDeviceVersion(INT32 deviceIndex, char *name, UINT32 nameLength) {
+    int ret = getMidiDeviceVersion(deviceIndex, name, nameLength);
+    return ret;
+}
+
+/*************************************************************************/
+
+INT32 MIDI_IN_OpenDevice(INT32 deviceIndex, MidiDeviceHandle** handle) {
+    INT32 ret;
+    TRACE0("> MIDI_IN_OpenDevice\n");
+    ret = openMidiDevice(SND_RAWMIDI_STREAM_INPUT, deviceIndex, handle);
+    TRACE1("< MIDI_IN_OpenDevice: returning %d\n", (int) ret);
+    return ret;
+}
+
+
+INT32 MIDI_IN_CloseDevice(MidiDeviceHandle* handle) {
+    INT32 ret;
+    TRACE0("> MIDI_IN_CloseDevice\n");
+    ret = closeMidiDevice(handle);
+    TRACE1("< MIDI_IN_CloseDevice: returning %d\n", (int) ret);
+    return ret;
+}
+
+
+INT32 MIDI_IN_StartDevice(MidiDeviceHandle* handle) {
+    TRACE0("MIDI_IN_StartDevice\n");
+    return MIDI_SUCCESS;
+}
+
+
+INT32 MIDI_IN_StopDevice(MidiDeviceHandle* handle) {
+    TRACE0("MIDI_IN_StopDevice\n");
+    return MIDI_SUCCESS;
+}
+
+
+INT64 MIDI_IN_GetTimeStamp(MidiDeviceHandle* handle) {
+    return getMidiTimestamp(handle);
+}
+
+
+/* read the next message from the queue */
+MidiMessage* MIDI_IN_GetMessage(MidiDeviceHandle* handle) {
+    snd_seq_event_t alsa_message;
+    MidiMessage* jdk_message;
+    int err;
+    char buffer[1];
+    int status;
+
+    TRACE0("> MIDI_IN_GetMessage\n");
+    if (!handle) {
+        ERROR0("< ERROR: MIDI_IN_GetMessage(): handle is NULL\n");
+        return NULL;
+    }
+    if (!handle->deviceHandle) {
+        ERROR0("< ERROR: MIDI_IN_GetMessage(): native handle is NULL\n");
+        return NULL;
+    }
+    if (!handle->platformData) {
+        ERROR0("< ERROR: MIDI_IN_GetMessage(): platformData is NULL\n");
+        return NULL;
+    }
+
+    /* For MIDI In, the device is left in non blocking mode. So if there is
+       no data from the device, snd_rawmidi_read() returns with -11 (EAGAIN).
+       This results in jumping back to the Java layer. */
+    while (TRUE) {
+        TRACE0("before snd_rawmidi_read()\n");
+        err = snd_rawmidi_read((snd_rawmidi_t*) handle->deviceHandle, buffer, 1);
+        TRACE0("after snd_rawmidi_read()\n");
+        if (err != 1) {
+            ERROR2("< ERROR: MIDI_IN_GetMessage(): snd_rawmidi_read() returned %d : %s\n", err, snd_strerror(err));
+            return NULL;
+        }
+        // printf("received byte: %d\n", buffer[0]);
+        err = snd_midi_event_encode_byte((snd_midi_event_t*) handle->platformData,
+                                         (int) buffer[0],
+                                         &alsa_message);
+        if (err == 1) {
+            break;
+        } else if (err < 0) {
+            ERROR1("< ERROR: MIDI_IN_GetMessage(): snd_midi_event_encode_byte() returned %d\n", err);
+            return NULL;
+        }
+    }
+    jdk_message = (MidiMessage*) calloc(sizeof(MidiMessage), 1);
+    if (!jdk_message) {
+        ERROR0("< ERROR: MIDI_IN_GetMessage(): out of memory\n");
+        return NULL;
+    }
+    // TODO: tra
+    switch (alsa_message.type) {
+    case SND_SEQ_EVENT_NOTEON:
+    case SND_SEQ_EVENT_NOTEOFF:
+    case SND_SEQ_EVENT_KEYPRESS:
+        status = (alsa_message.type == SND_SEQ_EVENT_KEYPRESS) ? 0xA0 :
+            (alsa_message.type == SND_SEQ_EVENT_NOTEON) ? 0x90 : 0x80;
+        status |= alsa_message.data.note.channel;
+        setShortMessage(jdk_message, status,
+                        alsa_message.data.note.note,
+                        alsa_message.data.note.velocity);
+        break;
+
+    case SND_SEQ_EVENT_CONTROLLER:
+        status = 0xB0 | alsa_message.data.control.channel;
+        setShortMessage(jdk_message, status,
+                        alsa_message.data.control.param,
+                        alsa_message.data.control.value);
+        break;
+
+    case SND_SEQ_EVENT_PGMCHANGE:
+    case SND_SEQ_EVENT_CHANPRESS:
+        status = (alsa_message.type == SND_SEQ_EVENT_PGMCHANGE) ? 0xC0 : 0xD0;
+        status |= alsa_message.data.control.channel;
+        setShortMessage(jdk_message, status,
+                        alsa_message.data.control.value, 0);
+        break;
+
+    case SND_SEQ_EVENT_PITCHBEND:
+        status = 0xE0 | alsa_message.data.control.channel;
+        // $$mp 2003-09-23:
+        // possible hack to work around a bug in ALSA. Necessary for
+        // ALSA 0.9.2. May be fixed in newer versions of ALSA.
+        // alsa_message.data.control.value ^= 0x2000;
+        // TRACE1("pitchbend value: %d\n", alsa_message.data.control.value);
+        set14bitMessage(jdk_message, status,
+                        alsa_message.data.control.value);
+        break;
+
+        /* System exclusive messages */
+
+    case SND_SEQ_EVENT_SYSEX:
+        jdk_message->type = LONG_MESSAGE;
+        jdk_message->data.l.size = alsa_message.data.ext.len;
+        jdk_message->data.l.data = malloc(alsa_message.data.ext.len);
+        if (jdk_message->data.l.data == NULL) {
+            ERROR0("< ERROR: MIDI_IN_GetMessage(): out of memory\n");
+            free(jdk_message);
+            jdk_message = NULL;
+        } else {
+            memcpy(jdk_message->data.l.data, alsa_message.data.ext.ptr, alsa_message.data.ext.len);
+        }
+        break;
+
+        /* System common messages */
+
+    case SND_SEQ_EVENT_QFRAME:
+        setShortMessage(jdk_message, 0xF1,
+                        alsa_message.data.control.value & 0x7F, 0);
+        break;
+
+    case SND_SEQ_EVENT_SONGPOS:
+        set14bitMessage(jdk_message, 0xF2,
+                        alsa_message.data.control.value);
+        break;
+
+    case SND_SEQ_EVENT_SONGSEL:
+        setShortMessage(jdk_message, 0xF3,
+                        alsa_message.data.control.value & 0x7F, 0);
+        break;
+
+    case SND_SEQ_EVENT_TUNE_REQUEST:
+        setRealtimeMessage(jdk_message, 0xF6);
+        break;
+
+        /* System realtime messages */
+
+    case SND_SEQ_EVENT_CLOCK:
+        setRealtimeMessage(jdk_message, 0xF8);
+        break;
+
+    case SND_SEQ_EVENT_START:
+        setRealtimeMessage(jdk_message, 0xFA);
+        break;
+
+    case SND_SEQ_EVENT_CONTINUE:
+        setRealtimeMessage(jdk_message, 0xFB);
+        break;
+
+    case SND_SEQ_EVENT_STOP:
+        setRealtimeMessage(jdk_message, 0xFC);
+        break;
+
+    case SND_SEQ_EVENT_SENSING:
+        setRealtimeMessage(jdk_message, 0xFE);
+        break;
+
+    case SND_SEQ_EVENT_RESET:
+        setRealtimeMessage(jdk_message, 0xFF);
+        break;
+
+    default:
+        ERROR0("< ERROR: MIDI_IN_GetMessage(): unhandled ALSA MIDI message type\n");
+        free(jdk_message);
+        jdk_message = NULL;
+
+    }
+
+    // set timestamp
+    if (jdk_message != NULL) {
+        jdk_message->timestamp = getMidiTimestamp(handle);
+    }
+    TRACE1("< MIDI_IN_GetMessage: returning %p\n", jdk_message);
+    return jdk_message;
+}
+
+
+void MIDI_IN_ReleaseMessage(MidiDeviceHandle* handle, MidiMessage* msg) {
+    if (!msg) {
+        ERROR0("< ERROR: MIDI_IN_ReleaseMessage(): message is NULL\n");
+        return;
+    }
+    if (msg->type == LONG_MESSAGE && msg->data.l.data) {
+        free(msg->data.l.data);
+    }
+    free(msg);
+}
+
+#endif /* USE_PLATFORM_MIDI_IN */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_MidiOut.c	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#define USE_ERROR
+#define USE_TRACE
+
+#if USE_PLATFORM_MIDI_OUT == TRUE
+
+#include <alsa/asoundlib.h>
+#include "PlatformMidi.h"
+#include "PLATFORM_API_BsdOS_ALSA_MidiUtils.h"
+
+
+
+static int CHANNEL_MESSAGE_LENGTH[] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 3, 3, 2, 2, 3 };
+/*                                 8x 9x Ax Bx Cx Dx Ex */
+
+static int SYSTEM_MESSAGE_LENGTH[] = {
+    -1, 2, 3, 2, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1 };
+/*  F0 F1 F2 F3  F4  F5 F6 F7 F8  F9 FA FB FC  FD FE FF */
+
+
+// the returned length includes the status byte.
+// for illegal messages, -1 is returned.
+static int getShortMessageLength(int status) {
+        int     dataLength = 0;
+        if (status < 0xF0) { // channel voice message
+                dataLength = CHANNEL_MESSAGE_LENGTH[(status >> 4) & 0xF];
+        } else {
+                dataLength = SYSTEM_MESSAGE_LENGTH[status & 0xF];
+        }
+        return dataLength;
+}
+
+
+/*
+ * implementation of the platform-dependent
+ * MIDI out functions declared in PlatformMidi.h
+ */
+char* MIDI_OUT_GetErrorStr(INT32 err) {
+    return (char*) getErrorStr(err);
+}
+
+
+INT32 MIDI_OUT_GetNumDevices() {
+    TRACE0("MIDI_OUT_GetNumDevices()\n");
+    return getMidiDeviceCount(SND_RAWMIDI_STREAM_OUTPUT);
+}
+
+
+INT32 MIDI_OUT_GetDeviceName(INT32 deviceIndex, char *name, UINT32 nameLength) {
+    TRACE0("MIDI_OUT_GetDeviceName()\n");
+    return getMidiDeviceName(SND_RAWMIDI_STREAM_OUTPUT, deviceIndex,
+                             name, nameLength);
+}
+
+
+INT32 MIDI_OUT_GetDeviceVendor(INT32 deviceIndex, char *name, UINT32 nameLength) {
+    TRACE0("MIDI_OUT_GetDeviceVendor()\n");
+    return getMidiDeviceVendor(deviceIndex, name, nameLength);
+}
+
+
+INT32 MIDI_OUT_GetDeviceDescription(INT32 deviceIndex, char *name, UINT32 nameLength) {
+    TRACE0("MIDI_OUT_GetDeviceDescription()\n");
+    return getMidiDeviceDescription(SND_RAWMIDI_STREAM_OUTPUT, deviceIndex,
+                                    name, nameLength);
+}
+
+
+INT32 MIDI_OUT_GetDeviceVersion(INT32 deviceIndex, char *name, UINT32 nameLength) {
+    TRACE0("MIDI_OUT_GetDeviceVersion()\n");
+    return getMidiDeviceVersion(deviceIndex, name, nameLength);
+}
+
+
+/* *************************** MidiOutDevice implementation *************** */
+
+INT32 MIDI_OUT_OpenDevice(INT32 deviceIndex, MidiDeviceHandle** handle) {
+    TRACE1("MIDI_OUT_OpenDevice(): deviceIndex: %d\n", (int) deviceIndex);
+    return openMidiDevice(SND_RAWMIDI_STREAM_OUTPUT, deviceIndex, handle);
+}
+
+
+INT32 MIDI_OUT_CloseDevice(MidiDeviceHandle* handle) {
+    TRACE0("MIDI_OUT_CloseDevice()\n");
+    return closeMidiDevice(handle);
+}
+
+
+INT64 MIDI_OUT_GetTimeStamp(MidiDeviceHandle* handle) {
+    return getMidiTimestamp(handle);
+}
+
+
+INT32 MIDI_OUT_SendShortMessage(MidiDeviceHandle* handle, UINT32 packedMsg,
+                                UINT32 timestamp) {
+    int err;
+    int status;
+    int data1;
+    int data2;
+    char buffer[3];
+
+    TRACE2("> MIDI_OUT_SendShortMessage() %x, time: %u\n", packedMsg, (unsigned int) timestamp);
+    if (!handle) {
+        ERROR0("< ERROR: MIDI_OUT_SendShortMessage(): handle is NULL\n");
+        return MIDI_INVALID_HANDLE;
+    }
+    if (!handle->deviceHandle) {
+        ERROR0("< ERROR: MIDI_OUT_SendLongMessage(): native handle is NULL\n");
+        return MIDI_INVALID_HANDLE;
+    }
+    status = (packedMsg & 0xFF);
+    buffer[0] = (char) status;
+    buffer[1]  = (char) ((packedMsg >> 8) & 0xFF);
+    buffer[2]  = (char) ((packedMsg >> 16) & 0xFF);
+    TRACE4("status: %d, data1: %d, data2: %d, length: %d\n", (int) buffer[0], (int) buffer[1], (int) buffer[2], getShortMessageLength(status));
+    err = snd_rawmidi_write((snd_rawmidi_t*) handle->deviceHandle, buffer, getShortMessageLength(status));
+    if (err < 0) {
+        ERROR1("  ERROR: MIDI_OUT_SendShortMessage(): snd_rawmidi_write() returned %d\n", err);
+    }
+
+    TRACE0("< MIDI_OUT_SendShortMessage()\n");
+    return err;
+}
+
+
+INT32 MIDI_OUT_SendLongMessage(MidiDeviceHandle* handle, UBYTE* data,
+                               UINT32 size, UINT32 timestamp) {
+    int err;
+
+    TRACE2("> MIDI_OUT_SendLongMessage() size %u, time: %u\n", (unsigned int) size, (unsigned int) timestamp);
+    if (!handle) {
+        ERROR0("< ERROR: MIDI_OUT_SendLongMessage(): handle is NULL\n");
+        return MIDI_INVALID_HANDLE;
+    }
+    if (!handle->deviceHandle) {
+        ERROR0("< ERROR: MIDI_OUT_SendLongMessage(): native handle is NULL\n");
+        return MIDI_INVALID_HANDLE;
+    }
+    if (!data) {
+        ERROR0("< ERROR: MIDI_OUT_SendLongMessage(): data is NULL\n");
+        return MIDI_INVALID_HANDLE;
+    }
+    err = snd_rawmidi_write((snd_rawmidi_t*) handle->deviceHandle,
+                            data, size);
+    if (err < 0) {
+        ERROR1("  ERROR: MIDI_OUT_SendLongMessage(): snd_rawmidi_write() returned %d\n", err);
+    }
+
+    TRACE0("< MIDI_OUT_SendLongMessage()\n");
+    return err;
+}
+
+
+#endif /* USE_PLATFORM_MIDI_OUT */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_MidiUtils.c	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#define USE_ERROR
+#define USE_TRACE
+
+#include "PLATFORM_API_BsdOS_ALSA_MidiUtils.h"
+#include "PLATFORM_API_BsdOS_ALSA_CommonUtils.h"
+#include <string.h>
+#include <sys/time.h>
+
+static INT64 getTimeInMicroseconds() {
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    return (tv.tv_sec * 1000000UL) + tv.tv_usec;
+}
+
+
+const char* getErrorStr(INT32 err) {
+        return snd_strerror((int) err);
+}
+
+
+
+// callback for iteration through devices
+// returns TRUE if iteration should continue
+typedef int (*DeviceIteratorPtr)(UINT32 deviceID,
+                                 snd_rawmidi_info_t* rawmidi_info,
+                                 snd_ctl_card_info_t* cardinfo,
+                                 void *userData);
+
+// for each ALSA device, call iterator. userData is passed to the iterator
+// returns total number of iterations
+static int iterateRawmidiDevices(snd_rawmidi_stream_t direction,
+                                 DeviceIteratorPtr iterator,
+                                 void* userData) {
+    int count = 0;
+    int subdeviceCount;
+    int card, dev, subDev;
+    char devname[16];
+    int err;
+    snd_ctl_t *handle;
+    snd_rawmidi_t *rawmidi;
+    snd_rawmidi_info_t *rawmidi_info;
+    snd_ctl_card_info_t *card_info, *defcardinfo = NULL;
+    UINT32 deviceID;
+    int doContinue = TRUE;
+
+    snd_rawmidi_info_malloc(&rawmidi_info);
+    snd_ctl_card_info_malloc(&card_info);
+
+    // 1st try "default" device
+    if (direction == SND_RAWMIDI_STREAM_INPUT) {
+        err = snd_rawmidi_open(&rawmidi, NULL, ALSA_DEFAULT_DEVICE_NAME,
+                               SND_RAWMIDI_NONBLOCK);
+    } else if (direction == SND_RAWMIDI_STREAM_OUTPUT) {
+        err = snd_rawmidi_open(NULL, &rawmidi, ALSA_DEFAULT_DEVICE_NAME,
+                               SND_RAWMIDI_NONBLOCK);
+    } else {
+        ERROR0("ERROR: iterateRawmidiDevices(): direction is neither"
+               " SND_RAWMIDI_STREAM_INPUT nor SND_RAWMIDI_STREAM_OUTPUT\n");
+        err = MIDI_INVALID_ARGUMENT;
+    }
+    if (err < 0) {
+        ERROR1("ERROR: snd_rawmidi_open (\"default\"): %s\n",
+               snd_strerror(err));
+    } else {
+        err = snd_rawmidi_info(rawmidi, rawmidi_info);
+
+        snd_rawmidi_close(rawmidi);
+        if (err < 0) {
+            ERROR1("ERROR: snd_rawmidi_info (\"default\"): %s\n",
+                    snd_strerror(err));
+        } else {
+            // try to get card info
+            card = snd_rawmidi_info_get_card(rawmidi_info);
+            if (card >= 0) {
+                sprintf(devname, ALSA_HARDWARE_CARD, card);
+                if (snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK) >= 0) {
+                    if (snd_ctl_card_info(handle, card_info) >= 0) {
+                        defcardinfo = card_info;
+                    }
+                    snd_ctl_close(handle);
+                }
+            }
+            // call calback function for the device
+            if (iterator != NULL) {
+                doContinue = (*iterator)(ALSA_DEFAULT_DEVICE_ID, rawmidi_info,
+                                         defcardinfo, userData);
+            }
+            count++;
+        }
+    }
+
+    // iterate cards
+    card = -1;
+    TRACE0("testing for cards...\n");
+    if (snd_card_next(&card) >= 0) {
+        TRACE1("Found card %d\n", card);
+        while (doContinue && (card >= 0)) {
+            sprintf(devname, ALSA_HARDWARE_CARD, card);
+            TRACE1("Opening control for alsa rawmidi device \"%s\"...\n", devname);
+            err = snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK);
+            if (err < 0) {
+                ERROR2("ERROR: snd_ctl_open, card=%d: %s\n", card, snd_strerror(err));
+            } else {
+                TRACE0("snd_ctl_open() SUCCESS\n");
+                err = snd_ctl_card_info(handle, card_info);
+                if (err < 0) {
+                    ERROR2("ERROR: snd_ctl_card_info, card=%d: %s\n", card, snd_strerror(err));
+                } else {
+                    TRACE0("snd_ctl_card_info() SUCCESS\n");
+                    dev = -1;
+                    while (doContinue) {
+                        if (snd_ctl_rawmidi_next_device(handle, &dev) < 0) {
+                            ERROR0("snd_ctl_rawmidi_next_device\n");
+                        }
+                        TRACE0("snd_ctl_rawmidi_next_device() SUCCESS\n");
+                        if (dev < 0) {
+                            break;
+                        }
+                        snd_rawmidi_info_set_device(rawmidi_info, dev);
+                        snd_rawmidi_info_set_subdevice(rawmidi_info, 0);
+                        snd_rawmidi_info_set_stream(rawmidi_info, direction);
+                        err = snd_ctl_rawmidi_info(handle, rawmidi_info);
+                        TRACE0("after snd_ctl_rawmidi_info()\n");
+                        if (err < 0) {
+                            if (err != -ENOENT) {
+                                ERROR2("ERROR: snd_ctl_rawmidi_info, card=%d: %s", card, snd_strerror(err));
+                            }
+                        } else {
+                            TRACE0("snd_ctl_rawmidi_info() SUCCESS\n");
+                            subdeviceCount = needEnumerateSubdevices(ALSA_RAWMIDI)
+                                ? snd_rawmidi_info_get_subdevices_count(rawmidi_info)
+                                : 1;
+                            if (iterator!=NULL) {
+                                for (subDev = 0; subDev < subdeviceCount; subDev++) {
+                                    TRACE3("  Iterating %d,%d,%d\n", card, dev, subDev);
+                                    deviceID = encodeDeviceID(card, dev, subDev);
+                                    doContinue = (*iterator)(deviceID, rawmidi_info,
+                                                             card_info, userData);
+                                    count++;
+                                    TRACE0("returned from iterator\n");
+                                    if (!doContinue) {
+                                        break;
+                                    }
+                                }
+                            } else {
+                                count += subdeviceCount;
+                            }
+                        }
+                    } // of while(doContinue)
+                }
+                snd_ctl_close(handle);
+            }
+            if (snd_card_next(&card) < 0) {
+                break;
+            }
+        }
+    } else {
+        ERROR0("No cards found!\n");
+    }
+    snd_ctl_card_info_free(card_info);
+    snd_rawmidi_info_free(rawmidi_info);
+    return count;
+}
+
+
+
+int getMidiDeviceCount(snd_rawmidi_stream_t direction) {
+    int deviceCount;
+    TRACE0("> getMidiDeviceCount()\n");
+    initAlsaSupport();
+    deviceCount = iterateRawmidiDevices(direction, NULL, NULL);
+    TRACE0("< getMidiDeviceCount()\n");
+    return deviceCount;
+}
+
+
+
+/*
+  userData is assumed to be a pointer to ALSA_MIDIDeviceDescription.
+  ALSA_MIDIDeviceDescription->index has to be set to the index of the device
+  we want to get information of before this method is called the first time via
+  iterateRawmidiDevices(). On each call of this method,
+  ALSA_MIDIDeviceDescription->index is decremented. If it is equal to zero,
+  we have reached the desired device, so action is taken.
+  So after successful completion of iterateRawmidiDevices(),
+  ALSA_MIDIDeviceDescription->index is zero. If it isn't, this is an
+  indication of an error.
+*/
+static int deviceInfoIterator(UINT32 deviceID, snd_rawmidi_info_t *rawmidi_info,
+                              snd_ctl_card_info_t *cardinfo, void *userData) {
+    char buffer[300];
+    ALSA_MIDIDeviceDescription* desc = (ALSA_MIDIDeviceDescription*)userData;
+#ifdef ALSA_MIDI_USE_PLUGHW
+    int usePlugHw = 1;
+#else
+    int usePlugHw = 0;
+#endif
+
+    TRACE0("deviceInfoIterator\n");
+    initAlsaSupport();
+    if (desc->index == 0) {
+        // we found the device with correct index
+        desc->deviceID = deviceID;
+
+        buffer[0]=' '; buffer[1]='[';
+        getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_RAWMIDI);
+        strcat(buffer, "]");
+        strncpy(desc->name,
+                (cardinfo != NULL)
+                    ? snd_ctl_card_info_get_id(cardinfo)
+                    : snd_rawmidi_info_get_id(rawmidi_info),
+                desc->strLen - strlen(buffer));
+        strncat(desc->name, buffer, desc->strLen - strlen(desc->name));
+        desc->description[0] = 0;
+        if (cardinfo != NULL) {
+            strncpy(desc->description, snd_ctl_card_info_get_name(cardinfo),
+                    desc->strLen);
+            strncat(desc->description, ", ",
+                    desc->strLen - strlen(desc->description));
+        }
+        strncat(desc->description, snd_rawmidi_info_get_id(rawmidi_info),
+                desc->strLen - strlen(desc->description));
+        strncat(desc->description, ", ", desc->strLen - strlen(desc->description));
+        strncat(desc->description, snd_rawmidi_info_get_name(rawmidi_info),
+                desc->strLen - strlen(desc->description));
+        TRACE2("Returning %s, %s\n", desc->name, desc->description);
+        return FALSE; // do not continue iteration
+    }
+    desc->index--;
+    return TRUE;
+}
+
+
+static int getMIDIDeviceDescriptionByIndex(snd_rawmidi_stream_t direction,
+                                           ALSA_MIDIDeviceDescription* desc) {
+    initAlsaSupport();
+    TRACE1(" getMIDIDeviceDescriptionByIndex (index = %d)\n", desc->index);
+    iterateRawmidiDevices(direction, &deviceInfoIterator, desc);
+    return (desc->index == 0) ? MIDI_SUCCESS : MIDI_INVALID_DEVICEID;
+}
+
+
+
+int initMIDIDeviceDescription(ALSA_MIDIDeviceDescription* desc, int index) {
+    int ret = MIDI_SUCCESS;
+    desc->index = index;
+    desc->strLen = 200;
+    desc->name = (char*) calloc(desc->strLen + 1, 1);
+    desc->description = (char*) calloc(desc->strLen + 1, 1);
+    if (! desc->name ||
+        ! desc->description) {
+        ret = MIDI_OUT_OF_MEMORY;
+    }
+    return ret;
+}
+
+
+void freeMIDIDeviceDescription(ALSA_MIDIDeviceDescription* desc) {
+    if (desc->name) {
+        free(desc->name);
+    }
+    if (desc->description) {
+        free(desc->description);
+    }
+}
+
+
+int getMidiDeviceName(snd_rawmidi_stream_t direction, int index, char *name,
+                      UINT32 nameLength) {
+    ALSA_MIDIDeviceDescription desc;
+    int ret;
+
+    TRACE1("getMidiDeviceName: nameLength: %d\n", (int) nameLength);
+    ret = initMIDIDeviceDescription(&desc, index);
+    if (ret == MIDI_SUCCESS) {
+        TRACE0("getMidiDeviceName: initMIDIDeviceDescription() SUCCESS\n");
+        ret = getMIDIDeviceDescriptionByIndex(direction, &desc);
+        if (ret == MIDI_SUCCESS) {
+            TRACE1("getMidiDeviceName: desc.name: %s\n", desc.name);
+            strncpy(name, desc.name, nameLength - 1);
+            name[nameLength - 1] = 0;
+        }
+    }
+    freeMIDIDeviceDescription(&desc);
+    return ret;
+}
+
+
+int getMidiDeviceVendor(int index, char *name, UINT32 nameLength) {
+    strncpy(name, ALSA_VENDOR, nameLength - 1);
+    name[nameLength - 1] = 0;
+    return MIDI_SUCCESS;
+}
+
+
+int getMidiDeviceDescription(snd_rawmidi_stream_t direction,
+                             int index, char *name, UINT32 nameLength) {
+    ALSA_MIDIDeviceDescription desc;
+    int ret;
+
+    ret = initMIDIDeviceDescription(&desc, index);
+    if (ret == MIDI_SUCCESS) {
+        ret = getMIDIDeviceDescriptionByIndex(direction, &desc);
+        if (ret == MIDI_SUCCESS) {
+            strncpy(name, desc.description, nameLength - 1);
+            name[nameLength - 1] = 0;
+        }
+    }
+    freeMIDIDeviceDescription(&desc);
+    return ret;
+}
+
+
+int getMidiDeviceVersion(int index, char *name, UINT32 nameLength) {
+    getALSAVersion(name, nameLength);
+    return MIDI_SUCCESS;
+}
+
+
+static int getMidiDeviceID(snd_rawmidi_stream_t direction, int index,
+                           UINT32* deviceID) {
+    ALSA_MIDIDeviceDescription desc;
+    int ret;
+
+    ret = initMIDIDeviceDescription(&desc, index);
+    if (ret == MIDI_SUCCESS) {
+        ret = getMIDIDeviceDescriptionByIndex(direction, &desc);
+        if (ret == MIDI_SUCCESS) {
+            // TRACE1("getMidiDeviceName: desc.name: %s\n", desc.name);
+            *deviceID = desc.deviceID;
+        }
+    }
+    freeMIDIDeviceDescription(&desc);
+    return ret;
+}
+
+
+/*
+  direction has to be either SND_RAWMIDI_STREAM_INPUT or
+  SND_RAWMIDI_STREAM_OUTPUT.
+  Returns 0 on success. Otherwise, MIDI_OUT_OF_MEMORY, MIDI_INVALID_ARGUMENT
+   or a negative ALSA error code is returned.
+*/
+INT32 openMidiDevice(snd_rawmidi_stream_t direction, INT32 deviceIndex,
+                     MidiDeviceHandle** handle) {
+    snd_rawmidi_t* native_handle;
+    snd_midi_event_t* event_parser = NULL;
+    int err;
+    UINT32 deviceID = 0;
+    char devicename[100];
+#ifdef ALSA_MIDI_USE_PLUGHW
+    int usePlugHw = 1;
+#else
+    int usePlugHw = 0;
+#endif
+
+    TRACE0("> openMidiDevice()\n");
+
+    (*handle) = (MidiDeviceHandle*) calloc(sizeof(MidiDeviceHandle), 1);
+    if (!(*handle)) {
+        ERROR0("ERROR: openDevice: out of memory\n");
+        return MIDI_OUT_OF_MEMORY;
+    }
+
+    // TODO: iterate to get dev ID from index
+    err = getMidiDeviceID(direction, deviceIndex, &deviceID);
+    TRACE1("  openMidiDevice(): deviceID: %d\n", (int) deviceID);
+    getDeviceStringFromDeviceID(devicename, deviceID,
+                                usePlugHw, ALSA_RAWMIDI);
+    TRACE1("  openMidiDevice(): deviceString: %s\n", devicename);
+
+    // finally open the device
+    if (direction == SND_RAWMIDI_STREAM_INPUT) {
+        err = snd_rawmidi_open(&native_handle, NULL, devicename,
+                               SND_RAWMIDI_NONBLOCK);
+    } else if (direction == SND_RAWMIDI_STREAM_OUTPUT) {
+        err = snd_rawmidi_open(NULL, &native_handle, devicename,
+                               SND_RAWMIDI_NONBLOCK);
+    } else {
+        ERROR0("  ERROR: openMidiDevice(): direction is neither SND_RAWMIDI_STREAM_INPUT nor SND_RAWMIDI_STREAM_OUTPUT\n");
+        err = MIDI_INVALID_ARGUMENT;
+    }
+    if (err < 0) {
+        ERROR1("<  ERROR: openMidiDevice(): snd_rawmidi_open() returned %d\n", err);
+        free(*handle);
+        (*handle) = NULL;
+        return err;
+    }
+    /* We opened with non-blocking behaviour to not get hung if the device
+       is used by a different process. Writing, however, should
+       be blocking. So we change it here. */
+    if (direction == SND_RAWMIDI_STREAM_OUTPUT) {
+        err = snd_rawmidi_nonblock(native_handle, 0);
+        if (err < 0) {
+            ERROR1("  ERROR: openMidiDevice(): snd_rawmidi_nonblock() returned %d\n", err);
+            snd_rawmidi_close(native_handle);
+            free(*handle);
+            (*handle) = NULL;
+            return err;
+        }
+    }
+    if (direction == SND_RAWMIDI_STREAM_INPUT) {
+        err = snd_midi_event_new(EVENT_PARSER_BUFSIZE, &event_parser);
+        if (err < 0) {
+            ERROR1("  ERROR: openMidiDevice(): snd_midi_event_new() returned %d\n", err);
+            snd_rawmidi_close(native_handle);
+            free(*handle);
+            (*handle) = NULL;
+            return err;
+        }
+    }
+
+    (*handle)->deviceHandle = (void*) native_handle;
+    (*handle)->startTime = getTimeInMicroseconds();
+    (*handle)->platformData = event_parser;
+    TRACE0("< openMidiDevice(): succeeded\n");
+    return err;
+}
+
+
+
+INT32 closeMidiDevice(MidiDeviceHandle* handle) {
+    int err;
+
+    TRACE0("> closeMidiDevice()\n");
+    if (!handle) {
+        ERROR0("< ERROR: closeMidiDevice(): handle is NULL\n");
+        return MIDI_INVALID_HANDLE;
+    }
+    if (!handle->deviceHandle) {
+        ERROR0("< ERROR: closeMidiDevice(): native handle is NULL\n");
+        return MIDI_INVALID_HANDLE;
+    }
+    err = snd_rawmidi_close((snd_rawmidi_t*) handle->deviceHandle);
+    TRACE1("  snd_rawmidi_close() returns %d\n", err);
+    if (handle->platformData) {
+        snd_midi_event_free((snd_midi_event_t*) handle->platformData);
+    }
+    free(handle);
+    TRACE0("< closeMidiDevice: succeeded\n");
+    return err;
+}
+
+
+INT64 getMidiTimestamp(MidiDeviceHandle* handle) {
+    if (!handle) {
+        ERROR0("< ERROR: closeMidiDevice(): handle is NULL\n");
+        return MIDI_INVALID_HANDLE;
+    }
+    return getTimeInMicroseconds() - handle->startTime;
+}
+
+
+/* end */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_MidiUtils.h	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <alsa/asoundlib.h>
+#include "Utilities.h"
+#include "PlatformMidi.h"
+
+
+#ifndef PLATFORM_API_BSDOS_ALSA_MIDIUTILS_H_INCLUDED
+#define PLATFORM_API_BSDOS_ALSA_MIDIUTILS_H_INCLUDED
+
+#define EVENT_PARSER_BUFSIZE (2048)
+
+// if this is defined, use plughw: devices
+//#define ALSA_MIDI_USE_PLUGHW
+#undef ALSA_MIDI_USE_PLUGHW
+
+typedef struct tag_ALSA_MIDIDeviceDescription {
+        int index;          // in
+        int strLen;         // in
+        INT32 deviceID;    // out
+        char* name;         // out
+        char* description;  // out
+} ALSA_MIDIDeviceDescription;
+
+
+const char* getErrorStr(INT32 err);
+
+/* Returns the number of devices. */
+/* direction is either SND_RAWMIDI_STREAM_OUTPUT or
+   SND_RAWMIDI_STREAM_INPUT. */
+int getMidiDeviceCount(snd_rawmidi_stream_t direction);
+
+/* Returns MIDI_SUCCESS or MIDI_INVALID_DEVICEID */
+/* direction is either SND_RAWMIDI_STREAM_OUTPUT or
+   SND_RAWMIDI_STREAM_INPUT. */
+int getMidiDeviceName(snd_rawmidi_stream_t direction, int index,
+                      char *name, UINT32 nameLength);
+
+/* Returns MIDI_SUCCESS or MIDI_INVALID_DEVICEID */
+int getMidiDeviceVendor(int index, char *name, UINT32 nameLength);
+
+/* Returns MIDI_SUCCESS or MIDI_INVALID_DEVICEID */
+/* direction is either SND_RAWMIDI_STREAM_OUTPUT or
+   SND_RAWMIDI_STREAM_INPUT. */
+int getMidiDeviceDescription(snd_rawmidi_stream_t direction, int index,
+                             char *name, UINT32 nameLength);
+
+/* Returns MIDI_SUCCESS or MIDI_INVALID_DEVICEID */
+int getMidiDeviceVersion(int index, char *name, UINT32 nameLength);
+
+// returns 0 on success, otherwise MIDI_OUT_OF_MEMORY or ALSA error code
+/* direction is either SND_RAWMIDI_STREAM_OUTPUT or
+   SND_RAWMIDI_STREAM_INPUT. */
+INT32 openMidiDevice(snd_rawmidi_stream_t direction, INT32 deviceIndex,
+                     MidiDeviceHandle** handle);
+
+// returns 0 on success, otherwise a (negative) ALSA error code
+INT32 closeMidiDevice(MidiDeviceHandle* handle);
+
+INT64 getMidiTimestamp(MidiDeviceHandle* handle);
+
+#endif // PLATFORM_API_BSDOS_ALSA_MIDIUTILS_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_PCM.c	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,896 @@
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#define USE_ERROR
+#define USE_TRACE
+
+#include "PLATFORM_API_BsdOS_ALSA_PCMUtils.h"
+#include "PLATFORM_API_BsdOS_ALSA_CommonUtils.h"
+#include "DirectAudio.h"
+
+#if USE_DAUDIO == TRUE
+
+// GetPosition method 1: based on how many bytes are passed to the kernel driver
+//                       + does not need much processor resources
+//                       - not very exact, "jumps"
+// GetPosition method 2: ask kernel about actual position of playback.
+//                       - very exact
+//                       - switch to kernel layer for each call
+// GetPosition method 3: use snd_pcm_avail() call - not yet in official ALSA
+// quick tests on a Pentium 200MMX showed max. 1.5% processor usage
+// for playing back a CD-quality file and printing 20x per second a line
+// on the console with the current time. So I guess performance is not such a
+// factor here.
+//#define GET_POSITION_METHOD1
+#define GET_POSITION_METHOD2
+
+
+// The default time for a period in microseconds.
+// For very small buffers, only 2 periods are used.
+#define DEFAULT_PERIOD_TIME 20000 /* 20ms */
+
+///// implemented functions of DirectAudio.h
+
+INT32 DAUDIO_GetDirectAudioDeviceCount() {
+    return (INT32) getAudioDeviceCount();
+}
+
+
+INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription* description) {
+    ALSA_AudioDeviceDescription adesc;
+
+    adesc.index = (int) mixerIndex;
+    adesc.strLen = DAUDIO_STRING_LENGTH;
+
+    adesc.maxSimultaneousLines = (int*) (&(description->maxSimulLines));
+    adesc.deviceID = &(description->deviceID);
+    adesc.name = description->name;
+    adesc.vendor = description->vendor;
+    adesc.description = description->description;
+    adesc.version = description->version;
+
+    return getAudioDeviceDescriptionByIndex(&adesc);
+}
+
+#define MAX_BIT_INDEX 6
+// returns
+// 6: for anything above 24-bit
+// 5: for 4 bytes sample size, 24-bit
+// 4: for 3 bytes sample size, 24-bit
+// 3: for 3 bytes sample size, 20-bit
+// 2: for 2 bytes sample size, 16-bit
+// 1: for 1 byte sample size, 8-bit
+// 0: for anything else
+int getBitIndex(int sampleSizeInBytes, int significantBits) {
+    if (significantBits > 24) return 6;
+    if (sampleSizeInBytes == 4 && significantBits == 24) return 5;
+    if (sampleSizeInBytes == 3) {
+        if (significantBits == 24) return 4;
+        if (significantBits == 20) return 3;
+    }
+    if (sampleSizeInBytes == 2 && significantBits == 16) return 2;
+    if (sampleSizeInBytes == 1 && significantBits == 8) return 1;
+    return 0;
+}
+
+int getSampleSizeInBytes(int bitIndex, int sampleSizeInBytes) {
+    switch(bitIndex) {
+    case 1: return 1;
+    case 2: return 2;
+    case 3: /* fall through */
+    case 4: return 3;
+    case 5: return 4;
+    }
+    return sampleSizeInBytes;
+}
+
+int getSignificantBits(int bitIndex, int significantBits) {
+    switch(bitIndex) {
+    case 1: return 8;
+    case 2: return 16;
+    case 3: return 20;
+    case 4: /* fall through */
+    case 5: return 24;
+    }
+    return significantBits;
+}
+
+void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {
+    snd_pcm_t* handle;
+    snd_pcm_format_mask_t* formatMask;
+    snd_pcm_format_t format;
+    snd_pcm_hw_params_t* hwParams;
+    int handledBits[MAX_BIT_INDEX+1];
+
+    int ret;
+    int sampleSizeInBytes, significantBits, isSigned, isBigEndian, enc;
+    int origSampleSizeInBytes, origSignificantBits;
+    unsigned int channels, minChannels, maxChannels;
+    int rate, bitIndex;
+
+    for (bitIndex = 0; bitIndex <= MAX_BIT_INDEX; bitIndex++) handledBits[bitIndex] = FALSE;
+    if (openPCMfromDeviceID(deviceID, &handle, isSource, TRUE /*query hardware*/) < 0) {
+        return;
+    }
+    ret = snd_pcm_format_mask_malloc(&formatMask);
+    if (ret != 0) {
+        ERROR1("snd_pcm_format_mask_malloc returned error %d\n", ret);
+    } else {
+        ret = snd_pcm_hw_params_malloc(&hwParams);
+        if (ret != 0) {
+            ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret);
+        } else {
+            ret = snd_pcm_hw_params_any(handle, hwParams);
+            /* snd_pcm_hw_params_any can return a positive value on success too */
+            if (ret < 0) {
+                 ERROR1("snd_pcm_hw_params_any returned error %d\n", ret);
+            } else {
+                /* for the logic following this code, set ret to 0 to indicate success */
+                ret = 0;
+            }
+        }
+        snd_pcm_hw_params_get_format_mask(hwParams, formatMask);
+        if (ret == 0) {
+            ret = snd_pcm_hw_params_get_channels_min(hwParams, &minChannels);
+            if (ret != 0) {
+                ERROR1("snd_pcm_hw_params_get_channels_min returned error %d\n", ret);
+            }
+        }
+        if (ret == 0) {
+            ret = snd_pcm_hw_params_get_channels_max(hwParams, &maxChannels);
+            if (ret != 0) {
+                ERROR1("snd_pcm_hw_params_get_channels_max returned error %d\n", ret);
+            }
+        }
+
+        // since we queried the hw: device, for many soundcards, it will only
+        // report the maximum number of channels (which is the only way to talk
+        // to the hw: device). Since we will, however, open the plughw: device
+        // when opening the Source/TargetDataLine, we can safely assume that
+        // also the channels 1..maxChannels are available.
+#ifdef ALSA_PCM_USE_PLUGHW
+        minChannels = 1;
+#endif
+        if (ret == 0) {
+            // plughw: supports any sample rate
+            rate = -1;
+            for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
+                if (snd_pcm_format_mask_test(formatMask, format)) {
+                    // format exists
+                    if (getFormatFromAlsaFormat(format, &origSampleSizeInBytes,
+                                                &origSignificantBits,
+                                                &isSigned, &isBigEndian, &enc)) {
+                        // now if we use plughw:, we can use any bit size below the
+                        // natively supported ones. Some ALSA drivers only support the maximum
+                        // bit size, so we add any sample rates below the reported one.
+                        // E.g. this iteration reports support for 16-bit.
+                        // getBitIndex will return 2, so it will add entries for
+                        // 16-bit (bitIndex=2) and in the next do-while loop iteration,
+                        // it will decrease bitIndex and will therefore add 8-bit support.
+                        bitIndex = getBitIndex(origSampleSizeInBytes, origSignificantBits);
+                        do {
+                            if (bitIndex == 0
+                                || bitIndex == MAX_BIT_INDEX
+                                || !handledBits[bitIndex]) {
+                                handledBits[bitIndex] = TRUE;
+                                sampleSizeInBytes = getSampleSizeInBytes(bitIndex, origSampleSizeInBytes);
+                                significantBits = getSignificantBits(bitIndex, origSignificantBits);
+                                if (maxChannels - minChannels > MAXIMUM_LISTED_CHANNELS) {
+                                    // avoid too many channels explicitly listed
+                                    // just add -1, min, and max
+                                    DAUDIO_AddAudioFormat(creator, significantBits,
+                                                          -1, -1, rate,
+                                                          enc, isSigned, isBigEndian);
+                                    DAUDIO_AddAudioFormat(creator, significantBits,
+                                                          sampleSizeInBytes * minChannels,
+                                                          minChannels, rate,
+                                                          enc, isSigned, isBigEndian);
+                                    DAUDIO_AddAudioFormat(creator, significantBits,
+                                                          sampleSizeInBytes * maxChannels,
+                                                          maxChannels, rate,
+                                                          enc, isSigned, isBigEndian);
+                                } else {
+                                    for (channels = minChannels; channels <= maxChannels; channels++) {
+                                        DAUDIO_AddAudioFormat(creator, significantBits,
+                                                              sampleSizeInBytes * channels,
+                                                              channels, rate,
+                                                              enc, isSigned, isBigEndian);
+                                    }
+                                }
+                            }
+#ifndef ALSA_PCM_USE_PLUGHW
+                            // without plugin, do not add fake formats
+                            break;
+#endif
+                        } while (--bitIndex > 0);
+                    } else {
+                        TRACE1("could not get format from alsa for format %d\n", format);
+                    }
+                } else {
+                    //TRACE1("Format %d not supported\n", format);
+                }
+            } // for loop
+            snd_pcm_hw_params_free(hwParams);
+        }
+        snd_pcm_format_mask_free(formatMask);
+    }
+    snd_pcm_close(handle);
+}
+
+/* ******* ALSA PCM INFO ******************** */
+typedef struct tag_AlsaPcmInfo {
+    snd_pcm_t* handle;
+    snd_pcm_hw_params_t* hwParams;
+    snd_pcm_sw_params_t* swParams;
+    int bufferSizeInBytes;
+    int frameSize; // storage size in Bytes
+    unsigned int periods;
+    snd_pcm_uframes_t periodSize;
+#ifdef GET_POSITION_METHOD2
+    // to be used exclusively by getBytePosition!
+    snd_pcm_status_t* positionStatus;
+#endif
+} AlsaPcmInfo;
+
+
+int setStartThresholdNoCommit(AlsaPcmInfo* info, int useThreshold) {
+    int ret;
+    int threshold;
+
+    if (useThreshold) {
+        // start device whenever anything is written to the buffer
+        threshold = 1;
+    } else {
+        // never start the device automatically
+        threshold = 2000000000; /* near UINT_MAX */
+    }
+    ret = snd_pcm_sw_params_set_start_threshold(info->handle, info->swParams, threshold);
+    if (ret < 0) {
+        ERROR1("Unable to set start threshold mode: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    return TRUE;
+}
+
+int setStartThreshold(AlsaPcmInfo* info, int useThreshold) {
+    int ret = 0;
+
+    if (!setStartThresholdNoCommit(info, useThreshold)) {
+        ret = -1;
+    }
+    if (ret == 0) {
+        // commit it
+        ret = snd_pcm_sw_params(info->handle, info->swParams);
+        if (ret < 0) {
+            ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
+        }
+    }
+    return (ret == 0)?TRUE:FALSE;
+}
+
+
+// returns TRUE if successful
+int setHWParams(AlsaPcmInfo* info,
+                float sampleRate,
+                int channels,
+                int bufferSizeInFrames,
+                snd_pcm_format_t format) {
+    unsigned int rrate, periodTime, periods;
+    int ret, dir;
+    snd_pcm_uframes_t alsaBufferSizeInFrames = (snd_pcm_uframes_t) bufferSizeInFrames;
+
+    /* choose all parameters */
+    ret = snd_pcm_hw_params_any(info->handle, info->hwParams);
+    if (ret < 0) {
+        ERROR1("Broken configuration: no configurations available: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    /* set the interleaved read/write format */
+    ret = snd_pcm_hw_params_set_access(info->handle, info->hwParams, SND_PCM_ACCESS_RW_INTERLEAVED);
+    if (ret < 0) {
+        ERROR1("SND_PCM_ACCESS_RW_INTERLEAVED access type not available: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    /* set the sample format */
+    ret = snd_pcm_hw_params_set_format(info->handle, info->hwParams, format);
+    if (ret < 0) {
+        ERROR1("Sample format not available: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    /* set the count of channels */
+    ret = snd_pcm_hw_params_set_channels(info->handle, info->hwParams, channels);
+    if (ret < 0) {
+        ERROR2("Channels count (%d) not available: %s\n", channels, snd_strerror(ret));
+        return FALSE;
+    }
+    /* set the stream rate */
+    rrate = (int) (sampleRate + 0.5f);
+    dir = 0;
+    ret = snd_pcm_hw_params_set_rate_near(info->handle, info->hwParams, &rrate, &dir);
+    if (ret < 0) {
+        ERROR2("Rate %dHz not available for playback: %s\n", (int) (sampleRate+0.5f), snd_strerror(ret));
+        return FALSE;
+    }
+    if ((rrate-sampleRate > 2) || (rrate-sampleRate < - 2)) {
+        ERROR2("Rate doesn't match (requested %2.2fHz, got %dHz)\n", sampleRate, rrate);
+        return FALSE;
+    }
+    /* set the buffer time */
+    ret = snd_pcm_hw_params_set_buffer_size_near(info->handle, info->hwParams, &alsaBufferSizeInFrames);
+    if (ret < 0) {
+        ERROR2("Unable to set buffer size to %d frames: %s\n",
+               (int) alsaBufferSizeInFrames, snd_strerror(ret));
+        return FALSE;
+    }
+    bufferSizeInFrames = (int) alsaBufferSizeInFrames;
+    /* set the period time */
+    if (bufferSizeInFrames > 1024) {
+        dir = 0;
+        periodTime = DEFAULT_PERIOD_TIME;
+        ret = snd_pcm_hw_params_set_period_time_near(info->handle, info->hwParams, &periodTime, &dir);
+        if (ret < 0) {
+            ERROR2("Unable to set period time to %d: %s\n", DEFAULT_PERIOD_TIME, snd_strerror(ret));
+            return FALSE;
+        }
+    } else {
+        /* set the period count for very small buffer sizes to 2 */
+        dir = 0;
+        periods = 2;
+        ret = snd_pcm_hw_params_set_periods_near(info->handle, info->hwParams, &periods, &dir);
+        if (ret < 0) {
+            ERROR2("Unable to set period count to %d: %s\n", /*periods*/ 2, snd_strerror(ret));
+            return FALSE;
+        }
+    }
+    /* write the parameters to device */
+    ret = snd_pcm_hw_params(info->handle, info->hwParams);
+    if (ret < 0) {
+        ERROR1("Unable to set hw params: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    return TRUE;
+}
+
+// returns 1 if successful
+int setSWParams(AlsaPcmInfo* info) {
+    int ret;
+
+    /* get the current swparams */
+    ret = snd_pcm_sw_params_current(info->handle, info->swParams);
+    if (ret < 0) {
+        ERROR1("Unable to determine current swparams: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    /* never start the transfer automatically */
+    if (!setStartThresholdNoCommit(info, FALSE /* don't use threshold */)) {
+        return FALSE;
+    }
+
+    /* allow the transfer when at least period_size samples can be processed */
+    ret = snd_pcm_sw_params_set_avail_min(info->handle, info->swParams, info->periodSize);
+    if (ret < 0) {
+        ERROR1("Unable to set avail min for playback: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    /* write the parameters to the playback device */
+    ret = snd_pcm_sw_params(info->handle, info->swParams);
+    if (ret < 0) {
+        ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static snd_output_t* ALSA_OUTPUT = NULL;
+
+void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
+                  int encoding, float sampleRate, int sampleSizeInBits,
+                  int frameSize, int channels,
+                  int isSigned, int isBigEndian, int bufferSizeInBytes) {
+    snd_pcm_format_mask_t* formatMask;
+    snd_pcm_format_t format;
+    int dir;
+    int ret = 0;
+    AlsaPcmInfo* info = NULL;
+    /* snd_pcm_uframes_t is 64 bit on 64-bit systems */
+    snd_pcm_uframes_t alsaBufferSizeInFrames = 0;
+
+
+    TRACE0("> DAUDIO_Open\n");
+#ifdef USE_TRACE
+    // for using ALSA debug dump methods
+    if (ALSA_OUTPUT == NULL) {
+        snd_output_stdio_attach(&ALSA_OUTPUT, stdout, 0);
+    }
+#endif
+
+    info = (AlsaPcmInfo*) malloc(sizeof(AlsaPcmInfo));
+    if (!info) {
+        ERROR0("Out of memory\n");
+        return NULL;
+    }
+    memset(info, 0, sizeof(AlsaPcmInfo));
+
+    ret = openPCMfromDeviceID(deviceID, &(info->handle), isSource, FALSE /* do open device*/);
+    if (ret == 0) {
+        // set to blocking mode
+        snd_pcm_nonblock(info->handle, 0);
+        ret = snd_pcm_hw_params_malloc(&(info->hwParams));
+        if (ret != 0) {
+            ERROR1("  snd_pcm_hw_params_malloc returned error %d\n", ret);
+        } else {
+            ret = -1;
+            if (getAlsaFormatFromFormat(&format, frameSize / channels, sampleSizeInBits,
+                                        isSigned, isBigEndian, encoding)) {
+                if (setHWParams(info,
+                                sampleRate,
+                                channels,
+                                bufferSizeInBytes / frameSize,
+                                format)) {
+                    info->frameSize = frameSize;
+                    ret = snd_pcm_hw_params_get_period_size(info->hwParams, &info->periodSize, &dir);
+                    if (ret < 0) {
+                        ERROR1("ERROR: snd_pcm_hw_params_get_period: %s\n", snd_strerror(ret));
+                    }
+                    snd_pcm_hw_params_get_periods(info->hwParams, &(info->periods), &dir);
+                    snd_pcm_hw_params_get_buffer_size(info->hwParams, &alsaBufferSizeInFrames);
+                    info->bufferSizeInBytes = (int) alsaBufferSizeInFrames * frameSize;
+                    TRACE3("  DAUDIO_Open: period size = %d frames, periods = %d. Buffer size: %d bytes.\n",
+                           (int) info->periodSize, info->periods, info->bufferSizeInBytes);
+                }
+            }
+        }
+        if (ret == 0) {
+            // set software parameters
+            ret = snd_pcm_sw_params_malloc(&(info->swParams));
+            if (ret != 0) {
+                ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret);
+            } else {
+                if (!setSWParams(info)) {
+                    ret = -1;
+                }
+            }
+        }
+        if (ret == 0) {
+            // prepare device
+            ret = snd_pcm_prepare(info->handle);
+            if (ret < 0) {
+                ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
+            }
+        }
+
+#ifdef GET_POSITION_METHOD2
+        if (ret == 0) {
+            ret = snd_pcm_status_malloc(&(info->positionStatus));
+            if (ret != 0) {
+                ERROR1("ERROR in snd_pcm_status_malloc: %s\n", snd_strerror(ret));
+            }
+        }
+#endif
+    }
+    if (ret != 0) {
+        DAUDIO_Close((void*) info, isSource);
+        info = NULL;
+    } else {
+        // set to non-blocking mode
+        snd_pcm_nonblock(info->handle, 1);
+        TRACE1("< DAUDIO_Open: Opened device successfully. Handle=%p\n",
+               (void*) info->handle);
+    }
+    return (void*) info;
+}
+
+#ifdef USE_TRACE
+void printState(snd_pcm_state_t state) {
+    if (state == SND_PCM_STATE_OPEN) {
+        TRACE0("State: SND_PCM_STATE_OPEN\n");
+    }
+    else if (state == SND_PCM_STATE_SETUP) {
+        TRACE0("State: SND_PCM_STATE_SETUP\n");
+    }
+    else if (state == SND_PCM_STATE_PREPARED) {
+        TRACE0("State: SND_PCM_STATE_PREPARED\n");
+    }
+    else if (state == SND_PCM_STATE_RUNNING) {
+        TRACE0("State: SND_PCM_STATE_RUNNING\n");
+    }
+    else if (state == SND_PCM_STATE_XRUN) {
+        TRACE0("State: SND_PCM_STATE_XRUN\n");
+    }
+    else if (state == SND_PCM_STATE_DRAINING) {
+        TRACE0("State: SND_PCM_STATE_DRAINING\n");
+    }
+    else if (state == SND_PCM_STATE_PAUSED) {
+        TRACE0("State: SND_PCM_STATE_PAUSED\n");
+    }
+    else if (state == SND_PCM_STATE_SUSPENDED) {
+        TRACE0("State: SND_PCM_STATE_SUSPENDED\n");
+    }
+}
+#endif
+
+int DAUDIO_Start(void* id, int isSource) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+    int ret;
+    snd_pcm_state_t state;
+
+    TRACE0("> DAUDIO_Start\n");
+    // set to blocking mode
+    snd_pcm_nonblock(info->handle, 0);
+    // set start mode so that it always starts as soon as data is there
+    setStartThreshold(info, TRUE /* use threshold */);
+    state = snd_pcm_state(info->handle);
+    if (state == SND_PCM_STATE_PAUSED) {
+        // in case it was stopped previously
+        TRACE0("  Un-pausing...\n");
+        ret = snd_pcm_pause(info->handle, FALSE);
+        if (ret != 0) {
+            ERROR2("  NOTE: error in snd_pcm_pause:%d: %s\n", ret, snd_strerror(ret));
+        }
+    }
+    if (state == SND_PCM_STATE_SUSPENDED) {
+        TRACE0("  Resuming...\n");
+        ret = snd_pcm_resume(info->handle);
+        if (ret < 0) {
+            if ((ret != -EAGAIN) && (ret != -ENOSYS)) {
+                ERROR2("  ERROR: error in snd_pcm_resume:%d: %s\n", ret, snd_strerror(ret));
+            }
+        }
+    }
+    if (state == SND_PCM_STATE_SETUP) {
+        TRACE0("need to call prepare again...\n");
+        // prepare device
+        ret = snd_pcm_prepare(info->handle);
+        if (ret < 0) {
+            ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
+        }
+    }
+    // in case there is still data in the buffers
+    ret = snd_pcm_start(info->handle);
+    if (ret != 0) {
+        if (ret != -EPIPE) {
+            ERROR2("  NOTE: error in snd_pcm_start: %d: %s\n", ret, snd_strerror(ret));
+        }
+    }
+    // set to non-blocking mode
+    ret = snd_pcm_nonblock(info->handle, 1);
+    if (ret != 0) {
+        ERROR1("  ERROR in snd_pcm_nonblock: %s\n", snd_strerror(ret));
+    }
+    state = snd_pcm_state(info->handle);
+#ifdef USE_TRACE
+    printState(state);
+#endif
+    ret = (state == SND_PCM_STATE_PREPARED)
+        || (state == SND_PCM_STATE_RUNNING)
+        || (state == SND_PCM_STATE_XRUN)
+        || (state == SND_PCM_STATE_SUSPENDED);
+    TRACE1("< DAUDIO_Start %s\n", ret?"success":"error");
+    return ret?TRUE:FALSE;
+}
+
+int DAUDIO_Stop(void* id, int isSource) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+    int ret;
+
+    TRACE0("> DAUDIO_Stop\n");
+    // set to blocking mode
+    snd_pcm_nonblock(info->handle, 0);
+    setStartThreshold(info, FALSE /* don't use threshold */); // device will not start after buffer xrun
+    ret = snd_pcm_pause(info->handle, 1);
+    // set to non-blocking mode
+    snd_pcm_nonblock(info->handle, 1);
+    if (ret != 0) {
+        ERROR1("ERROR in snd_pcm_pause: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    TRACE0("< DAUDIO_Stop success\n");
+    return TRUE;
+}
+
+void DAUDIO_Close(void* id, int isSource) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+
+    TRACE0("DAUDIO_Close\n");
+    if (info != NULL) {
+        if (info->handle != NULL) {
+            snd_pcm_close(info->handle);
+        }
+        if (info->hwParams) {
+            snd_pcm_hw_params_free(info->hwParams);
+        }
+        if (info->swParams) {
+            snd_pcm_sw_params_free(info->swParams);
+        }
+#ifdef GET_POSITION_METHOD2
+        if (info->positionStatus) {
+            snd_pcm_status_free(info->positionStatus);
+        }
+#endif
+        free(info);
+    }
+}
+
+/*
+ * Underrun and suspend recovery
+ * returns
+ * 0:  exit native and return 0
+ * 1:  try again to write/read
+ * -1: error - exit native with return value -1
+ */
+int xrun_recovery(AlsaPcmInfo* info, int err) {
+    int ret;
+
+    if (err == -EPIPE) {    /* underrun / overflow */
+        TRACE0("xrun_recovery: underrun/overflow.\n");
+        ret = snd_pcm_prepare(info->handle);
+        if (ret < 0) {
+            ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
+            return -1;
+        }
+        return 1;
+    }
+    else if (err == -ESTRPIPE) {
+        TRACE0("xrun_recovery: suspended.\n");
+        ret = snd_pcm_resume(info->handle);
+        if (ret < 0) {
+            if (ret == -EAGAIN) {
+                return 0; /* wait until the suspend flag is released */
+            }
+            return -1;
+        }
+        ret = snd_pcm_prepare(info->handle);
+        if (ret < 0) {
+            ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
+            return -1;
+        }
+        return 1;
+    }
+    else if (err == -EAGAIN) {
+        TRACE0("xrun_recovery: EAGAIN try again flag.\n");
+        return 0;
+    }
+    TRACE2("xrun_recovery: unexpected error %d: %s\n", err, snd_strerror(err));
+    return -1;
+}
+
+// returns -1 on error
+int DAUDIO_Write(void* id, char* data, int byteSize) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+    int ret, count;
+    snd_pcm_sframes_t frameSize, writtenFrames;
+
+    TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
+
+    /* sanity */
+    if (byteSize <= 0 || info->frameSize <= 0) {
+        ERROR2(" DAUDIO_Write: byteSize=%d, frameSize=%d!\n",
+               (int) byteSize, (int) info->frameSize);
+        TRACE0("< DAUDIO_Write returning -1\n");
+        return -1;
+    }
+    count = 2; // maximum number of trials to recover from underrun
+    //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
+    frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
+    do {
+        writtenFrames = snd_pcm_writei(info->handle, (const void*) data, (snd_pcm_uframes_t) frameSize);
+
+        if (writtenFrames < 0) {
+            ret = xrun_recovery(info, (int) writtenFrames);
+            if (ret <= 0) {
+                TRACE1("DAUDIO_Write: xrun recovery returned %d -> return.\n", ret);
+                return ret;
+            }
+            if (count-- <= 0) {
+                ERROR0("DAUDIO_Write: too many attempts to recover from xrun/suspend\n");
+                return -1;
+            }
+        } else {
+            break;
+        }
+    } while (TRUE);
+    //ret =  snd_pcm_frames_to_bytes(info->handle, writtenFrames);
+    ret =  (int) (writtenFrames * info->frameSize);
+    TRACE1("< DAUDIO_Write: returning %d bytes.\n", ret);
+    return ret;
+}
+
+// returns -1 on error
+int DAUDIO_Read(void* id, char* data, int byteSize) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+    int ret, count;
+    snd_pcm_sframes_t frameSize, readFrames;
+
+    TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
+    /*TRACE3("  info=%p, data=%p, byteSize=%d\n",
+      (void*) info, (void*) data, (int) byteSize);
+      TRACE2("  info->frameSize=%d, info->handle=%p\n",
+      (int) info->frameSize, (void*) info->handle);
+    */
+    /* sanity */
+    if (byteSize <= 0 || info->frameSize <= 0) {
+        ERROR2(" DAUDIO_Read: byteSize=%d, frameSize=%d!\n",
+               (int) byteSize, (int) info->frameSize);
+        TRACE0("< DAUDIO_Read returning -1\n");
+        return -1;
+    }
+    count = 2; // maximum number of trials to recover from error
+    //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
+    frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
+    do {
+        readFrames = snd_pcm_readi(info->handle, (void*) data, (snd_pcm_uframes_t) frameSize);
+        if (readFrames < 0) {
+            ret = xrun_recovery(info, (int) readFrames);
+            if (ret <= 0) {
+                TRACE1("DAUDIO_Read: xrun recovery returned %d -> return.\n", ret);
+                return ret;
+            }
+            if (count-- <= 0) {
+                ERROR0("DAUDIO_Read: too many attempts to recover from xrun/suspend\n");
+                return -1;
+            }
+        } else {
+            break;
+        }
+    } while (TRUE);
+    //ret =  snd_pcm_frames_to_bytes(info->handle, readFrames);
+    ret =  (int) (readFrames * info->frameSize);
+    TRACE1("< DAUDIO_Read: returning %d bytes.\n", ret);
+    return ret;
+}
+
+
+int DAUDIO_GetBufferSize(void* id, int isSource) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+
+    return info->bufferSizeInBytes;
+}
+
+int DAUDIO_StillDraining(void* id, int isSource) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+    snd_pcm_state_t state;
+
+    state = snd_pcm_state(info->handle);
+    //printState(state);
+    //TRACE1("Still draining: %s\n", (state != SND_PCM_STATE_XRUN)?"TRUE":"FALSE");
+    return (state == SND_PCM_STATE_RUNNING)?TRUE:FALSE;
+}
+
+
+int DAUDIO_Flush(void* id, int isSource) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+    int ret;
+
+    TRACE0("DAUDIO_Flush\n");
+    ret = snd_pcm_drop(info->handle);
+    if (ret != 0) {
+        ERROR1("ERROR in snd_pcm_drop: %s\n", snd_strerror(ret));
+        return FALSE;
+    }
+    ret = DAUDIO_Start(id, isSource);
+    return ret;
+}
+
+int DAUDIO_GetAvailable(void* id, int isSource) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+    snd_pcm_sframes_t availableInFrames;
+    snd_pcm_state_t state;
+    int ret;
+
+    state = snd_pcm_state(info->handle);
+    if (state == SND_PCM_STATE_XRUN) {
+        // if in xrun state then we have the entire buffer available,
+        // not 0 as alsa reports
+        ret = info->bufferSizeInBytes;
+    } else {
+        availableInFrames = snd_pcm_avail_update(info->handle);
+        if (availableInFrames < 0) {
+            ret = 0;
+        } else {
+            //ret = snd_pcm_frames_to_bytes(info->handle, availableInFrames);
+            ret = (int) (availableInFrames * info->frameSize);
+        }
+    }
+    TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);
+    return ret;
+}
+
+INT64 estimatePositionFromAvail(AlsaPcmInfo* info, int isSource, INT64 javaBytePos, int availInBytes) {
+    // estimate the current position with the buffer size and
+    // the available bytes to read or write in the buffer.
+    // not an elegant solution - bytePos will stop on xruns,
+    // and in race conditions it may jump backwards
+    // Advantage is that it is indeed based on the samples that go through
+    // the system (rather than time-based methods)
+    if (isSource) {
+        // javaBytePos is the position that is reached when the current
+        // buffer is played completely
+        return (INT64) (javaBytePos - info->bufferSizeInBytes + availInBytes);
+    } else {
+        // javaBytePos is the position that was when the current buffer was empty
+        return (INT64) (javaBytePos + availInBytes);
+    }
+}
+
+INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
+    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
+    int ret;
+    INT64 result = javaBytePos;
+    snd_pcm_state_t state;
+    state = snd_pcm_state(info->handle);
+
+    if (state != SND_PCM_STATE_XRUN) {
+#ifdef GET_POSITION_METHOD2
+        snd_timestamp_t* ts;
+        snd_pcm_uframes_t framesAvail;
+
+        // note: slight race condition if this is called simultaneously from 2 threads
+        ret = snd_pcm_status(info->handle, info->positionStatus);
+        if (ret != 0) {
+            ERROR1("ERROR in snd_pcm_status: %s\n", snd_strerror(ret));
+            result = javaBytePos;
+        } else {
+            // calculate from time value, or from available bytes
+            framesAvail = snd_pcm_status_get_avail(info->positionStatus);
+            result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize);
+        }
+#endif
+#ifdef GET_POSITION_METHOD3
+        snd_pcm_uframes_t framesAvail;
+        ret = snd_pcm_avail(info->handle, &framesAvail);
+        if (ret != 0) {
+            ERROR1("ERROR in snd_pcm_avail: %s\n", snd_strerror(ret));
+            result = javaBytePos;
+        } else {
+            result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize);
+        }
+#endif
+#ifdef GET_POSITION_METHOD1
+        result = estimatePositionFromAvail(info, isSource, javaBytePos, DAUDIO_GetAvailable(id, isSource));
+#endif
+    }
+    //printf("getbyteposition: javaBytePos=%d , return=%d\n", (int) javaBytePos, (int) result);
+    return result;
+}
+
+
+
+void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
+    /* save to ignore, since GetBytePosition
+     * takes the javaBytePos param into account
+     */
+}
+
+int DAUDIO_RequiresServicing(void* id, int isSource) {
+    // never need servicing on Bsd
+    return FALSE;
+}
+
+void DAUDIO_Service(void* id, int isSource) {
+    // never need servicing on Bsd
+}
+
+
+#endif // USE_DAUDIO
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_PCMUtils.c	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+//#define USE_ERROR
+//#define USE_TRACE
+
+#include "PLATFORM_API_BsdOS_ALSA_PCMUtils.h"
+#include "PLATFORM_API_BsdOS_ALSA_CommonUtils.h"
+
+
+
+// callback for iteration through devices
+// returns TRUE if iteration should continue
+// NOTE: cardinfo may be NULL (for "default" device)
+typedef int (*DeviceIteratorPtr)(UINT32 deviceID, snd_pcm_info_t* pcminfo,
+                             snd_ctl_card_info_t* cardinfo, void *userData);
+
+// for each ALSA device, call iterator. userData is passed to the iterator
+// returns total number of iterations
+int iteratePCMDevices(DeviceIteratorPtr iterator, void* userData) {
+    int count = 0;
+    int subdeviceCount;
+    int card, dev, subDev;
+    char devname[16];
+    int err;
+    snd_ctl_t *handle;
+    snd_pcm_t *pcm;
+    snd_pcm_info_t* pcminfo;
+    snd_ctl_card_info_t *cardinfo, *defcardinfo = NULL;
+    UINT32 deviceID;
+    int doContinue = TRUE;
+
+    snd_pcm_info_malloc(&pcminfo);
+    snd_ctl_card_info_malloc(&cardinfo);
+
+    // 1st try "default" device
+    err = snd_pcm_open(&pcm, ALSA_DEFAULT_DEVICE_NAME,
+                       SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+    if (err < 0) {
+        // try with the other direction
+        err = snd_pcm_open(&pcm, ALSA_DEFAULT_DEVICE_NAME,
+                           SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
+    }
+    if (err < 0) {
+        ERROR1("ERROR: snd_pcm_open (\"default\"): %s\n", snd_strerror(err));
+    } else {
+        err = snd_pcm_info(pcm, pcminfo);
+        snd_pcm_close(pcm);
+        if (err < 0) {
+            ERROR1("ERROR: snd_pcm_info (\"default\"): %s\n",
+                    snd_strerror(err));
+        } else {
+            // try to get card info
+            card = snd_pcm_info_get_card(pcminfo);
+            if (card >= 0) {
+                sprintf(devname, ALSA_HARDWARE_CARD, card);
+                if (snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK) >= 0) {
+                    if (snd_ctl_card_info(handle, cardinfo) >= 0) {
+                        defcardinfo = cardinfo;
+                    }
+                    snd_ctl_close(handle);
+                }
+            }
+            // call callback function for the device
+            if (iterator != NULL) {
+                doContinue = (*iterator)(ALSA_DEFAULT_DEVICE_ID, pcminfo,
+                                         defcardinfo, userData);
+            }
+            count++;
+        }
+    }
+
+    // iterate cards
+    card = -1;
+    while (doContinue) {
+        if (snd_card_next(&card) < 0) {
+            break;
+        }
+        if (card < 0) {
+            break;
+        }
+        sprintf(devname, ALSA_HARDWARE_CARD, card);
+        TRACE1("Opening alsa device \"%s\"...\n", devname);
+        err = snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK);
+        if (err < 0) {
+            ERROR2("ERROR: snd_ctl_open, card=%d: %s\n",
+                    card, snd_strerror(err));
+        } else {
+            err = snd_ctl_card_info(handle, cardinfo);
+            if (err < 0) {
+                ERROR2("ERROR: snd_ctl_card_info, card=%d: %s\n",
+                        card, snd_strerror(err));
+            } else {
+                dev = -1;
+                while (doContinue) {
+                    if (snd_ctl_pcm_next_device(handle, &dev) < 0) {
+                        ERROR0("snd_ctl_pcm_next_device\n");
+                    }
+                    if (dev < 0) {
+                        break;
+                    }
+                    snd_pcm_info_set_device(pcminfo, dev);
+                    snd_pcm_info_set_subdevice(pcminfo, 0);
+                    snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
+                    err = snd_ctl_pcm_info(handle, pcminfo);
+                    if (err == -ENOENT) {
+                        // try with the other direction
+                        snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);
+                        err = snd_ctl_pcm_info(handle, pcminfo);
+                    }
+                    if (err < 0) {
+                        if (err != -ENOENT) {
+                            ERROR2("ERROR: snd_ctl_pcm_info, card=%d: %s",
+                                    card, snd_strerror(err));
+                        }
+                    } else {
+                        subdeviceCount = needEnumerateSubdevices(ALSA_PCM) ?
+                            snd_pcm_info_get_subdevices_count(pcminfo) : 1;
+                        if (iterator!=NULL) {
+                            for (subDev = 0; subDev < subdeviceCount; subDev++) {
+                                deviceID = encodeDeviceID(card, dev, subDev);
+                                doContinue = (*iterator)(deviceID, pcminfo,
+                                                         cardinfo, userData);
+                                count++;
+                                if (!doContinue) {
+                                    break;
+                                }
+                            }
+                        } else {
+                            count += subdeviceCount;
+                        }
+                    }
+                } // of while(doContinue)
+            }
+            snd_ctl_close(handle);
+        }
+    }
+    snd_ctl_card_info_free(cardinfo);
+    snd_pcm_info_free(pcminfo);
+    return count;
+}
+
+int getAudioDeviceCount() {
+    initAlsaSupport();
+    return iteratePCMDevices(NULL, NULL);
+}
+
+int deviceInfoIterator(UINT32 deviceID, snd_pcm_info_t* pcminfo,
+                       snd_ctl_card_info_t* cardinfo, void* userData) {
+    char buffer[300];
+    ALSA_AudioDeviceDescription* desc = (ALSA_AudioDeviceDescription*)userData;
+#ifdef ALSA_PCM_USE_PLUGHW
+    int usePlugHw = 1;
+#else
+    int usePlugHw = 0;
+#endif
+
+    initAlsaSupport();
+    if (desc->index == 0) {
+        // we found the device with correct index
+        *(desc->maxSimultaneousLines) = needEnumerateSubdevices(ALSA_PCM) ?
+                1 : snd_pcm_info_get_subdevices_count(pcminfo);
+        *desc->deviceID = deviceID;
+        buffer[0]=' '; buffer[1]='[';
+        getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_PCM);
+        strcat(buffer, "]");
+        strncpy(desc->name,
+                (cardinfo != NULL)
+                    ? snd_ctl_card_info_get_id(cardinfo)
+                    : snd_pcm_info_get_id(pcminfo),
+                desc->strLen - strlen(buffer));
+        strncat(desc->name, buffer, desc->strLen - strlen(desc->name));
+        strncpy(desc->vendor, "ALSA (http://www.alsa-project.org)", desc->strLen);
+        strncpy(desc->description,
+                (cardinfo != NULL)
+                    ? snd_ctl_card_info_get_name(cardinfo)
+                    : snd_pcm_info_get_name(pcminfo),
+                desc->strLen);
+        strncat(desc->description, ", ", desc->strLen - strlen(desc->description));
+        strncat(desc->description, snd_pcm_info_get_id(pcminfo), desc->strLen - strlen(desc->description));
+        strncat(desc->description, ", ", desc->strLen - strlen(desc->description));
+        strncat(desc->description, snd_pcm_info_get_name(pcminfo), desc->strLen - strlen(desc->description));
+        getALSAVersion(desc->version, desc->strLen);
+        TRACE4("Returning %s, %s, %s, %s\n", desc->name, desc->vendor, desc->description, desc->version);
+        return FALSE; // do not continue iteration
+    }
+    desc->index--;
+    return TRUE;
+}
+
+// returns 0 if successful
+int openPCMfromDeviceID(int deviceID, snd_pcm_t** handle, int isSource, int hardware) {
+    char buffer[200];
+    int ret;
+
+    initAlsaSupport();
+    getDeviceStringFromDeviceID(buffer, deviceID, !hardware, ALSA_PCM);
+
+    TRACE1("Opening ALSA device %s\n", buffer);
+    ret = snd_pcm_open(handle, buffer,
+                       isSource?SND_PCM_STREAM_PLAYBACK:SND_PCM_STREAM_CAPTURE,
+                       SND_PCM_NONBLOCK);
+    if (ret != 0) {
+        ERROR1("snd_pcm_open returned error code %d \n", ret);
+        *handle = NULL;
+    }
+    return ret;
+}
+
+
+int getAudioDeviceDescriptionByIndex(ALSA_AudioDeviceDescription* desc) {
+    initAlsaSupport();
+    TRACE1(" getAudioDeviceDescriptionByIndex(mixerIndex = %d\n", desc->index);
+    iteratePCMDevices(&deviceInfoIterator, desc);
+    return (desc->index == 0)?TRUE:FALSE;
+}
+
+// returns 1 if successful
+// enc: 0 for PCM, 1 for ULAW, 2 for ALAW (see DirectAudio.h)
+int getFormatFromAlsaFormat(snd_pcm_format_t alsaFormat,
+                            int* sampleSizeInBytes, int* significantBits,
+                            int* isSigned, int* isBigEndian, int* enc) {
+
+    *sampleSizeInBytes = (snd_pcm_format_physical_width(alsaFormat) + 7) / 8;
+    *significantBits = snd_pcm_format_width(alsaFormat);
+
+    // defaults
+    *enc = 0; // PCM
+    *isSigned = (snd_pcm_format_signed(alsaFormat) > 0);
+    *isBigEndian = (snd_pcm_format_big_endian(alsaFormat) > 0);
+
+    // non-PCM formats
+    if (alsaFormat == SND_PCM_FORMAT_MU_LAW) { // Mu-Law
+        *sampleSizeInBytes = 8; *enc = 1; *significantBits = *sampleSizeInBytes;
+    }
+    else if (alsaFormat == SND_PCM_FORMAT_A_LAW) {     // A-Law
+        *sampleSizeInBytes = 8; *enc = 2; *significantBits = *sampleSizeInBytes;
+    }
+    else if (snd_pcm_format_linear(alsaFormat) < 1) {
+        return 0;
+    }
+    return (*sampleSizeInBytes > 0);
+}
+
+// returns 1 if successful
+int getAlsaFormatFromFormat(snd_pcm_format_t* alsaFormat,
+                            int sampleSizeInBytes, int significantBits,
+                            int isSigned, int isBigEndian, int enc) {
+    *alsaFormat = SND_PCM_FORMAT_UNKNOWN;
+
+    if (enc == 0) {
+        *alsaFormat = snd_pcm_build_linear_format(significantBits,
+                                                  sampleSizeInBytes * 8,
+                                                  isSigned?0:1,
+                                                  isBigEndian?1:0);
+    }
+    else if ((sampleSizeInBytes == 1) && (significantBits == 8)) {
+        if (enc == 1) { // ULAW
+            *alsaFormat = SND_PCM_FORMAT_MU_LAW;
+        }
+        else if (enc == 2) { // ALAW
+            *alsaFormat = SND_PCM_FORMAT_A_LAW;
+        }
+    }
+    return (*alsaFormat == SND_PCM_FORMAT_UNKNOWN)?0:1;
+}
+
+
+/* end */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_PCMUtils.h	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// define this with a later version of ALSA than 0.9.0rc3
+// (starting from 1.0.0 it became default behaviour)
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#include <alsa/asoundlib.h>
+#include "Utilities.h"
+
+#ifndef PLATFORM_API_BSDOS_ALSA_PCMUTILS_H_INCLUDED
+#define PLATFORM_API_BSDOS_ALSA_PCMUTILS_H_INCLUDED
+
+// if this is defined, use plughw: devices
+#define ALSA_PCM_USE_PLUGHW
+//#undef ALSA_PCM_USE_PLUGHW
+
+
+// maximum number of channels that is listed in the formats. If more, than
+// just -1 for channel count is used.
+#define MAXIMUM_LISTED_CHANNELS 32
+
+typedef struct tag_ALSA_AudioDeviceDescription {
+    int index;          // in
+    int strLen;         // in
+    INT32* deviceID;    // out
+    int* maxSimultaneousLines; // out
+    char* name;         // out
+    char* vendor;       // out
+    char* description;  // out
+    char* version;      // out
+} ALSA_AudioDeviceDescription;
+
+
+
+int getAudioDeviceCount();
+int getAudioDeviceDescriptionByIndex(ALSA_AudioDeviceDescription* desc);
+
+// returns ALSA error code, or 0 if successful
+int openPCMfromDeviceID(int deviceID, snd_pcm_t** handle, int isSource, int hardware);
+
+// returns 1 if successful
+// enc: 0 for PCM, 1 for ULAW, 2 for ALAW (see DirectAudio.h)
+int getFormatFromAlsaFormat(snd_pcm_format_t alsaFormat,
+                            int* sampleSizeInBytes, int* significantBits,
+                            int* isSigned, int* isBigEndian, int* enc);
+
+int getAlsaFormatFromFormat(snd_pcm_format_t* alsaFormat,
+                            int sampleSizeInBytes, int significantBits,
+                            int isSigned, int isBigEndian, int enc);
+
+#endif // PLATFORM_API_BSDOS_ALSA_PCMUTILS_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_BsdOS_ALSA_Ports.c	Sun Apr 03 10:33:53 2011 -0700
@@ -0,0 +1,723 @@
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#define USE_ERROR
+//#define USE_TRACE
+
+#include "Ports.h"
+#include "PLATFORM_API_BsdOS_ALSA_CommonUtils.h"
+#include <alsa/asoundlib.h>
+
+#if USE_PORTS == TRUE
+
+#define MAX_ELEMS (300)
+#define MAX_CONTROLS (MAX_ELEMS * 4)
+
+#define CHANNELS_MONO (SND_MIXER_SCHN_LAST + 1)
+#define CHANNELS_STEREO (SND_MIXER_SCHN_LAST + 2)
+
+typedef struct {
+    snd_mixer_elem_t* elem;
+    INT32 portType; /* one of PORT_XXX_xx */
+    char* controlType; /* one of CONTROL_TYPE_xx */
+    /* Values: either SND_MIXER_SCHN_FRONT_xx, CHANNELS_MONO or CHANNELS_STEREO.
+       For SND_MIXER_SCHN_FRONT_xx, exactly this channel is set/retrieved directly.
+       For CHANNELS_MONO, ALSA channel SND_MIXER_SCHN_MONO is set/retrieved directly.
+       For CHANNELS_STEREO, ALSA channels SND_MIXER_SCHN_FRONT_LEFT and SND_MIXER_SCHN_FRONT_RIGHT
+       are set after a calculation that takes balance into account. Retrieved? Average of both
+       channels? (Using a cached value is not a good idea since the value in the HW may have been
+       altered.) */
+    INT32 channel;
+} PortControl;
+
+
+typedef struct tag_PortMixer {
+    snd_mixer_t* mixer_handle;
+    /* Number of array elements used in elems and types. */
+    int numElems;
+    snd_mixer_elem_t** elems;
+    /* Array of port types (PORT_SRC_UNKNOWN etc.). Indices are the same as in elems. */
+    INT32* types;
+    /* Number of array elements used in controls. */
+    int numControls;
+    PortControl* controls;
+} PortMixer;
+
+
+///// implemented functions of Ports.h
+
+INT32 PORT_GetPortMixerCount() {
+    INT32 mixerCount;
+    int card;
+    char devname[16];
+    int err;
+    snd_ctl_t *handle;
+    snd_ctl_card_info_t* info;
+
+    TRACE0("> PORT_GetPortMixerCount\n");
+
+    initAlsaSupport();
+
+    snd_ctl_card_info_malloc(&info);
+    card = -1;
+    mixerCount = 0;
+    if (snd_card_next(&card) >= 0) {
+        while (card >= 0) {
+            sprintf(devname, ALSA_HARDWARE_CARD, card);
+            TRACE1("PORT_GetPortMixerCount: Opening alsa device \"%s\"...\n", devname);
+            err = snd_ctl_open(&handle, devname, 0);
+            if (err < 0) {
+                ERROR2("ERROR: snd_ctl_open, card=%d: %s\n", card, snd_strerror(err));
+            } else {
+                mixerCount++;
+                snd_ctl_close(handle);
+            }
+            if (snd_card_next(&card) < 0) {
+                break;
+            }
+        }
+    }
+    snd_ctl_card_info_free(info);
+    TRACE0("< PORT_GetPortMixerCount\n");
+    return mixerCount;
+}
+
+
+INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* description) {
+    snd_ctl_t* handle;
+    snd_ctl_card_info_t* card_info;
+    char devname[16];
+    int err;
+    char buffer[100];
+
+    TRACE0("> PORT_GetPortMixerDescription\n");
+    snd_ctl_card_info_malloc(&card_info);
+
+    sprintf(devname, ALSA_HARDWARE_CARD, (int) mixerIndex);
+    TRACE1("Opening alsa device \"%s\"...\n", devname);
+    err = snd_ctl_open(&handle, devname, 0);
+    if (err < 0) {
+        ERROR2("ERROR: snd_ctl_open, card=%d: %s\n", (int) mixerIndex, snd_strerror(err));
+        return FALSE;
+    }
+    err = snd_ctl_card_info(handle, card_info);
+    if (err < 0) {
+        ERROR2("ERROR: snd_ctl_card_info, card=%d: %s\n", (int) mixerIndex, snd_strerror(err));
+    }
+    strncpy(description->name, snd_ctl_card_info_get_id(card_info), PORT_STRING_LENGTH - 1);
+    sprintf(buffer, " [%s]", devname);
+    strncat(description->name, buffer, PORT_STRING_LENGTH - 1 - strlen(description->name));
+    strncpy(description->vendor, "ALSA (http://www.alsa-project.org)", PORT_STRING_LENGTH - 1);
+    strncpy(description->description, snd_ctl_card_info_get_name(card_info), PORT_STRING_LENGTH - 1);
+    strncat(description->description, ", ", PORT_STRING_LENGTH - 1 - strlen(description->description));
+    strncat(description->description, snd_ctl_card_info_get_mixername(card_info), PORT_STRING_LENGTH - 1 - strlen(description->description));
+    getALSAVersion(description->version, PORT_STRING_LENGTH - 1);
+
+    snd_ctl_close(handle);
+    snd_ctl_card_info_free(card_info);
+    TRACE0("< PORT_GetPortMixerDescription\n");
+    return TRUE;
+}
+
+
+void* PORT_Open(INT32 mixerIndex) {
+    char devname[16];
+    snd_mixer_t* mixer_handle;
+    int err;
+    PortMixer* handle;
+
+    TRACE0("> PORT_Open\n");
+    sprintf(devname, ALSA_HARDWARE_CARD, (int) mixerIndex);
+    if ((err = snd_mixer_open(&mixer_handle, 0)) < 0) {
+        ERROR2("Mixer %s open error: %s", devname, snd_strerror(err));
+        return NULL;
+    }
+    if ((err = snd_mixer_attach(mixer_handle, devname)) < 0) {
+        ERROR2("Mixer attach %s error: %s", devname, snd_strerror(err));
+        snd_mixer_close(mixer_handle);
+        return NULL;
+    }
+    if ((err = snd_mixer_selem_register(mixer_handle, NULL, NULL)) < 0) {
+        ERROR1("Mixer register error: %s", snd_strerror(err));
+        snd_mixer_close(mixer_handle);
+        return NULL;
+    }
+    err = snd_mixer_load(mixer_handle);
+    if (err < 0) {
+        ERROR2("Mixer %s load error: %s", devname, snd_strerror(err));
+        snd_mixer_close(mixer_handle);
+        return NULL;
+    }
+    handle = (PortMixer*) calloc(1, sizeof(PortMixer));
+    if (handle == NULL) {
+        ERROR0("malloc() failed.");
+        snd_mixer_close(mixer_handle);
+        return NULL;
+    }
+    handle->numElems = 0;
+    handle->elems = (snd_mixer_elem_t**) calloc(MAX_ELEMS, sizeof(snd_mixer_elem_t*));
+    if (handle->elems == NULL) {
+        ERROR0("malloc() failed.");
+        snd_mixer_close(mixer_handle);
+        free(handle);
+        return NULL;
+    }
+    handle->types = (INT32*) calloc(MAX_ELEMS, sizeof(INT32));
+    if (handle->types == NULL) {
+        ERROR0("malloc() failed.");
+        snd_mixer_close(mixer_handle);
+        free(handle->elems);
+        free(handle);
+        return NULL;
+    }
+    handle->controls = (PortControl*) calloc(MAX_CONTROLS, sizeof(PortControl));
+    if (handle->controls == NULL) {
+        ERROR0("malloc() failed.");
+        snd_mixer_close(mixer_handle);
+        free(handle->elems);
+        free(handle->types);
+        free(handle);
+        return NULL;
+    }
+    handle->mixer_handle = mixer_handle;
+    // necessary to initialize data structures
+    PORT_GetPortCount(handle);
+    TRACE0("< PORT_Open\n");
+    return handle;
+}
+
+
+void PORT_Close(void* id) {
+    TRACE0("> PORT_Close\n");
+    if (id != NULL) {
+        PortMixer* handle = (PortMixer*) id;
+        if (handle->mixer_handle != NULL) {
+            snd_mixer_close(handle->mixer_handle);
+        }
+        if (handle->elems != NULL) {
+            free(handle->elems);
+        }
+        if (handle->types != NULL) {
+            free(handle->types);
+        }
+        if (handle->controls != NULL) {
+            free(handle->controls);
+        }
+        free(handle);
+    }
+    TRACE0("< PORT_Close\n");
+}
+
+
+
+INT32 PORT_GetPortCount(void* id) {
+    PortMixer* portMixer;
+    snd_mixer_elem_t *elem;
+
+    TRACE0("> PORT_GetPortCount\n");
+    if (id == NULL) {
+        // $$mp: Should become a descriptive error code (invalid handle).
+        return -1;
+    }
+    portMixer = (PortMixer*) id;
+    if (portMixer->numElems == 0) {
+        for (elem = snd_mixer_first_elem(portMixer->mixer_handle); elem; elem = snd_mixer_elem_next(elem)) {
+            if (!snd_mixer_selem_is_active(elem))
+                continue;
+            TRACE2("Simple mixer control '%s',%i\n",
+                   snd_mixer_selem_get_name(elem),
+                   snd_mixer_selem_get_index(elem));
+            if (snd_mixer_selem_has_playback_volume(elem)) {
+                portMixer->elems[portMixer->numElems] = elem;
+                portMixer->types[portMixer->numElems] = PORT_DST_UNKNOWN;
+                portMixer->numElems++;
+            }
+            // to prevent buffer overflow
+            if (portMixer->numElems >= MAX_ELEMS) {
+                break;
+            }
+            /* If an element has both playback an capture volume, it is put into the arrays
+               twice. */
+            if (snd_mixer_selem_has_capture_volume(elem)) {
+                portMixer->elems[portMixer->numElems] = elem;
+                portMixer->types[portMixer->numElems] = PORT_SRC_UNKNOWN;
+                portMixer->numElems++;
+            }
+            // to prevent buffer overflow
+            if (portMixer->numElems >= MAX_ELEMS) {
+                break;
+            }
+        }
+    }
+    TRACE0("< PORT_GetPortCount\n");
+    return portMixer->numElems;
+}
+
+
+INT32 PORT_GetPortType(void* id, INT32 portIndex) {
+    PortMixer* portMixer;
+    INT32 type;
+    TRACE0("> PORT_GetPortType\n");
+    if (id == NULL) {
+        // $$mp: Should become a descriptive error code (invalid handle).
+        return -1;
+    }
+    portMixer = (PortMixer*) id;
+    if (portIndex < 0 || portIndex >= portMixer->numElems) {
+        // $$mp: Should become a descriptive error code (index out of bounds).
+        return -1;
+    }
+    type = portMixer->types[portIndex];
+    TRACE0("< PORT_GetPortType\n");
+    return type;
+}
+
+
+INT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) {
+    PortMixer* portMixer;
+    const char* nam;
+
+    TRACE0("> PORT_GetPortName\n");
+    if (id == NULL) {
+        // $$mp: Should become a descriptive error code (invalid handle).
+        return -1;
+    }
+    portMixer = (PortMixer*) id;
+    if (portIndex < 0 || portIndex >= portMixer->numElems) {
+        // $$mp: Should become a descriptive error code (index out of bounds).
+        return -1;
+    }
+    nam = snd_mixer_selem_get_name(portMixer->elems[portIndex]);
+    strncpy(name, nam, len - 1);
+    name[len - 1] = 0;
+    TRACE0("< PORT_GetPortName\n");
+    return TRUE;
+}
+
+
+static int isPlaybackFunction(INT32 portType) {
+        return (portType & PORT_DST_MASK);
+}
+
+
+/* Sets portControl to a pointer to the next free array element in the PortControl (pointer)
+   array of the passed portMixer. Returns TRUE if successful. May return FALSE if there is no
+   free slot. In this case, portControl is not altered */
+static int getControlSlot(PortMixer* portMixer, PortControl** portControl) {
+    if (portMixer->numControls >= MAX_CONTROLS) {
+        return FALSE;
+    } else {
+        *portControl = &(portMixer->controls[portMixer->numControls]);
+        portMixer->numControls++;
+        return TRUE;
+    }
+}
+
+
+/* Protect against illegal min-max values, preventing divisions by zero.
+ */
+inline static long getRange(long min, long max) {
+    if (max > min) {
+        return max - min;
+    } else {
+        return 1;
+    }
+}
+
+
+/* Idea: we may specify that if unit is an empty string, the values are linear and if unit is "dB",
+   the values are logarithmic.
+*/
+static void* createVolumeControl(PortControlCreator* creator,
+                                 PortControl* portControl,
+                                 snd_mixer_elem_t* elem, int isPlayback) {
+    void* control;
+    float precision;
+    long min, max;
+
+    if (isPlayback) {
+        snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
+    } else {
+        snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
+    }
+    /* $$mp: The volume values retrieved with the ALSA API are strongly supposed to be logarithmic.
+       So the following calculation is wrong. However, there is no correct calculation, since
+       for equal-distant logarithmic steps, the precision expressed in linear varies over the
+       scale. */
+    precision = 1.0F / getRange(min, max);
+    control = (creator->newFloatControl)(creator, portControl, CONTROL_TYPE_VOLUME, 0.0F, +1.0F, precision, "");
+    return control;
+}
+
+
+void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
+    PortMixer* portMixer;
+    snd_mixer_elem_t* elem;
+    void* control;
+    PortControl* portControl;
+    void* controls[10];
+    int numControls;
+    char* portName;
+    int isPlayback = 0;
+    int isMono;
+    int isStereo;
+    char* type;
+    snd_mixer_selem_channel_id_t channel;
+
+    TRACE0("> PORT_GetControls\n");
+    if (id == NULL) {
+        ERROR0("Invalid handle!");
+        // $$mp: an error code should be returned.
+        return;
+    }
+    portMixer = (PortMixer*) id;
+    if (portIndex < 0 || portIndex >= portMixer->numElems) {
+        ERROR0("Port index out of range!");
+        // $$mp: an error code should be returned.
+        return;
+    }
+    numControls = 0;
+    elem = portMixer->elems[portIndex];
+    if (snd_mixer_selem_has_playback_volume(elem) || snd_mixer_selem_has_capture_volume(elem)) {
+        /* Since we've splitted/duplicated elements with both playback and capture on the recovery
+           of elements, we now can assume that we handle only to deal with either playback or
+           capture. */
+        isPlayback = isPlaybackFunction(portMixer->types[portIndex]);
+        isMono = (isPlayback && snd_mixer_selem_is_playback_mono(elem)) ||
+            (!isPlayback && snd_mixer_selem_is_capture_mono(elem));
+        isStereo = (isPlayback &&
+                    snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_FRONT_LEFT) &&
+                    snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_FRONT_RIGHT)) ||
+            (!isPlayback &&
+             snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_FRONT_LEFT) &&
+             snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_FRONT_RIGHT));
+        // single volume control
+        if (isMono || isStereo) {
+            if (getControlSlot(portMixer, &portControl)) {
+                portControl->elem = elem;
+                portControl->portType = portMixer->types[portIndex];
+                portControl->controlType = CONTROL_TYPE_VOLUME;
+                if (isMono) {
+                    portControl->channel = CHANNELS_MONO;
+                } else {
+                    portControl->channel = CHANNELS_STEREO;
+                }
+                control = createVolumeControl(creator, portControl, elem, isPlayback);
+                if (control != NULL) {
+                    controls[numControls++] = control;
+                }
+            }
+        } else { // more than two channels, each channels has its own control.
+            for (channel = SND_MIXER_SCHN_FRONT_LEFT; channel <= SND_MIXER_SCHN_LAST; channel++) {
+                if (isPlayback && snd_mixer_selem_has_playback_channel(elem, channel) ||
+                    !isPlayback && snd_mixer_selem_has_capture_channel(elem, channel)) {
+                    if (getControlSlot(portMixer, &portControl)) {
+                        portControl->elem = elem;
+                        portControl->portType = portMixer->types[portIndex];
+                        portControl->controlType = CONTROL_TYPE_VOLUME;
+                        portControl->channel = channel;
+                        control = createVolumeControl(creator, portControl, elem, isPlayback);
+                        // We wrap in a compound control to provide the channel name.
+                        if (control != NULL) {
+                            /* $$mp 2003-09-14: The following cast shouln't be necessary. Instead, the
+                               declaration of PORT_NewCompoundControlPtr in Ports.h should be changed
+                               to take a const char* parameter. */
+                            control = (creator->newCompoundControl)(creator, (char*) snd_mixer_selem_channel_name(channel), &control, 1);
+                        }
+                        if (control != NULL) {
+                            controls[numControls++] = control;
+                        }
+                    }
+                }
+            }
+        }
+        // BALANCE control
+        if (isStereo) {
+            if (getControlSlot(portMixer, &portControl)) {
+                portControl->elem = elem;
+                portControl->portType = portMixer->types[portIndex];
+                portControl->controlType = CONTROL_TYPE_BALANCE;
+                portControl->channel = CHANNELS_STEREO;
+                /* $$mp: The value for precision is chosen more or less arbitrarily. */
+                control = (creator->newFloatControl)(creator, portControl, CONTROL_TYPE_BALANCE, -1.0F, 1.0F, 0.01F, "");
+                if (control != NULL) {
+                    controls[numControls++] = control;
+                }
+            }
+        }
+    }
+    if (snd_mixer_selem_has_playback_switch(elem) || snd_mixer_selem_has_capture_switch(elem)) {
+        if (getControlSlot(portMixer, &portControl)) {
+            type = isPlayback ? CONTROL_TYPE_MUTE : CONTROL_TYPE_SELECT;
+            portControl->elem = elem;
+            portControl->portType = portMixer->types[portIndex];
+            portControl->controlType = type;
+            control = (creator->newBooleanControl)(creator, portControl, type);
+            if (control != NULL) {
+                controls[numControls++] = control;
+            }
+        }
+    }
+    /* $$mp 2003-09-14: The following cast shouln't be necessary. Instead, the
+       declaration of PORT_NewCompoundControlPtr in Ports.h should be changed
+       to take a const char* parameter. */
+    portName = (char*) snd_mixer_selem_get_name(elem);
+    control = (creator->newCompoundControl)(creator, portName, controls, numControls);
+    if (control != NULL) {
+        (creator->addControl)(creator, control);
+    }
+    TRACE0("< PORT_GetControls\n");
+}
+
+
+INT32 PORT_GetIntValue(void* controlIDV) {
+    PortControl* portControl = (PortControl*) controlIDV;
+    int value = 0;
+    snd_mixer_selem_channel_id_t channel;
+
+    if (portControl != NULL) {
+        switch (portControl->channel) {
+        case CHANNELS_MONO:
+            channel = SND_MIXER_SCHN_MONO;
+            break;
+
+        case CHANNELS_STEREO:
+            channel = SND_MIXER_SCHN_FRONT_LEFT;
+            break;
+
+        default:
+            channel = portControl->channel;
+        }
+        if (portControl->controlType == CONTROL_TYPE_MUTE ||
+            portControl->controlType == CONTROL_TYPE_SELECT) {
+            if (isPlaybackFunction(portControl->portType)) {
+                snd_mixer_selem_get_playback_switch(portControl->elem, channel, &value);
+            } else {
+                snd_mixer_selem_get_capture_switch(portControl->elem, channel, &value);
+            }
+            if (portControl->controlType == CONTROL_TYPE_MUTE) {
+                value = ! value;
+            }
+        } else {
+            ERROR1("PORT_GetIntValue(): inappropriate control type: %s\n",
+                   portControl->controlType);
+        }
+    }
+    return (INT32) value;
+}
+
+
+void PORT_SetIntValue(void* controlIDV, INT32 value) {
+    PortControl* portControl = (PortControl*) controlIDV;
+    snd_mixer_selem_channel_id_t channel;
+
+    if (portControl != NULL) {
+        if (portControl->controlType == CONTROL_TYPE_MUTE) {
+            value = ! value;
+        }
+        if (portControl->controlType == CONTROL_TYPE_MUTE ||
+            portControl->controlType == CONTROL_TYPE_SELECT) {
+            if (isPlaybackFunction(portControl->portType)) {
+                snd_mixer_selem_set_playback_switch_all(portControl->elem, value);
+            } else {
+                snd_mixer_selem_set_capture_switch_all(portControl->elem, value);
+            }
+        } else {
+            ERROR1("PORT_SetIntValue(): inappropriate control type: %s\n",
+                   portControl->controlType);
+        }
+    }
+}
+
+
+static float scaleVolumeValueToNormalized(long value, long min, long max) {
+    return (float) (value - min) / getRange(min, max);
+}
+
+
+static long scaleVolumeValueToHardware(float value, long min, long max) {
+    return (long)(value * getRange(min, max) + min);
+}
+
+
+float getRealVolume(PortControl* portControl,
+                    snd_mixer_selem_channel_id_t channel) {
+    float fValue;
+    long lValue = 0;
+    long min = 0;
+    long max = 0;
+
+    if (isPlaybackFunction(portControl->portType)) {
+        snd_mixer_selem_get_playback_volume_range(portControl->elem,
+                                                  &min, &max);
+        snd_mixer_selem_get_playback_volume(portControl->elem,
+                                            channel, &lValue);
+    } else {
+        snd_mixer_selem_get_capture_volume_range(portControl->elem,
+                                                 &min, &max);
+        snd_mixer_selem_get_capture_volume(portControl->elem,
+                                           channel, &lValue);
+    }
+    fValue = scaleVolumeValueToNormalized(lValue, min, max);
+    return fValue;
+}
+
+
+void setRealVolume(PortControl* portControl,
+                   snd_mixer_selem_channel_id_t channel, float value) {
+    long lValue = 0;
+    long min = 0;
+    long max = 0;
+
+    if (isPlaybackFunction(portControl->portType)) {
+        snd_mixer_selem_get_playback_volume_range(portControl->elem,
+                                                  &min, &max);
+        lValue = scaleVolumeValueToHardware(value, min, max);
+        snd_mixer_selem_set_playback_volume(portControl->elem,
+                                            channel, lValue);
+    } else {
+        snd_mixer_selem_get_capture_volume_range(portControl->elem,
+                                                 &min, &max);
+        lValue = scaleVolumeValueToHardware(value, min, max);
+        snd_mixer_selem_set_capture_volume(portControl->elem,
+                                           channel, lValue);
+    }
+}
+
+
+static float getFakeBalance(PortControl* portControl) {
+    float volL, volR;
+
+    // pan is the ratio of left and right
+    volL = getRealVolume(portControl, SND_MIXER_SCHN_FRONT_LEFT);
+    volR = getRealVolume(portControl, SND_MIXER_SCHN_FRONT_RIGHT);
+    if (volL > volR) {
+        return -1.0f + (volR / volL);
+    }
+    else if (volR > volL) {
+        return 1.0f - (volL / volR);
+    }
+    return 0.0f;
+}
+
+
+static float getFakeVolume(PortControl* portControl) {
+    float valueL;
+    float valueR;
+    float value;
+
+    valueL = getRealVolume(portControl, SND_MIXER_SCHN_FRONT_LEFT);
+    valueR = getRealVolume(portControl, SND_MIXER_SCHN_FRONT_RIGHT);
+    // volume is the greater value of both
+    value = valueL > valueR ? valueL : valueR ;
+    return value;
+}
+
+
+/*
+ * sets the unsigned values for left and right volume according to
+ * the given volume (0...1) and balance (-1..0..+1)
+ */
+static void setFakeVolume(PortControl* portControl, float vol, float bal) {
+    float volumeLeft;
+    float volumeRight;
+
+    if (bal < 0.0f) {
+        volumeLeft = vol;
+        volumeRight = vol * (bal + 1.0f);
+    } else {
+        volumeLeft = vol * (1.0f - bal);
+        volumeRight = vol;
+    }
+    setRealVolume(portControl, SND_MIXER_SCHN_FRONT_LEFT, volumeLeft);
+    setRealVolume(portControl, SND_MIXER_SCHN_FRONT_RIGHT, volumeRight);
+}
+
+
+float PORT_GetFloatValue(void* controlIDV) {
+    PortControl* portControl = (PortControl*) controlIDV;
+    float value = 0.0F;
+
+    if (portControl != NULL) {
+        if (portControl->controlType == CONTROL_TYPE_VOLUME) {
+            switch (portControl->channel) {
+            case CHANNELS_MONO:
+                value = getRealVolume(portControl, SND_MIXER_SCHN_MONO);
+                break;
+
+            case CHANNELS_STEREO:
+                value = getFakeVolume(portControl);
+                break;
+
+            default:
+                value = getRealVolume(portControl, portControl->channel);
+            }
+        } else if (portControl->controlType == CONTROL_TYPE_BALANCE) {
+            if (portControl->channel == CHANNELS_STEREO) {
+                value = getFakeBalance(portControl);
+            } else {
+                ERROR0("PORT_GetFloatValue(): Balance only allowed for stereo channels!\n");
+            }
+        } else {
+            ERROR1("PORT_GetFloatValue(): inappropriate control type: %s!\n",
+                   portControl->controlType);
+        }
+    }
+    return value;
+}
+
+
+void PORT_SetFloatValue(void* controlIDV, float value) {
+    PortControl* portControl = (PortControl*) controlIDV;
+
+    if (portControl != NULL) {
+        if (portControl->controlType == CONTROL_TYPE_VOLUME) {
+            switch (portControl->channel) {
+            case CHANNELS_MONO:
+                setRealVolume(portControl, SND_MIXER_SCHN_MONO, value);
+                break;
+
+            case CHANNELS_STEREO:
+                setFakeVolume(portControl, value, getFakeBalance(portControl));
+                break;
+
+            default:
+                setRealVolume(portControl, portControl->channel, value);
+            }
+        } else if (portControl->controlType == CONTROL_TYPE_BALANCE) {
+            if (portControl->channel == CHANNELS_STEREO) {
+                setFakeVolume(portControl, getFakeVolume(portControl), value);
+            } else {
+                ERROR0("PORT_SetFloatValue(): Balance only allowed for stereo channels!\n");
+            }
+        } else {
+            ERROR1("PORT_SetFloatValue(): inappropriate control type: %s!\n",
+                   portControl->controlType);
+        }
+    }
+}
+
+
+#endif // USE_PORTS