8071469: Cleanup include and exclude of sound native libraries

Reviewed-by: amenkov, erikj
This commit is contained in:
Magnus Ihse Bursie 2018-03-23 09:51:02 +01:00
parent 3e5b58e0d7
commit de23920e05
29 changed files with 48 additions and 3763 deletions

View File

@ -23,101 +23,50 @@
# questions.
#
LIBJSOUND_SRC_DIRS := \
LIBJSOUND_SRC_DIRS := $(wildcard \
$(TOPDIR)/src/java.desktop/share/native/libjsound \
$(TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/libjsound \
#
$(TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS)/native/libjsound \
)
LIBJSOUND_CFLAGS := \
-I$(SUPPORT_OUTPUTDIR)/headers/java.desktop \
$(LIBJAVA_HEADER_FLAGS) \
$(foreach dir, $(LIBJSOUND_SRC_DIRS), -I$(dir)) \
-DUSE_PORTS=TRUE \
-DUSE_DAUDIO=TRUE \
#
LIBJSOUND_SRC_FILES := Utilities.c Platform.c
EXTRA_SOUND_JNI_LIBS :=
LIBJSOUND_MIDIFILES := \
MidiInDevice.c \
MidiInDeviceProvider.c \
MidiOutDevice.c \
MidiOutDeviceProvider.c \
PlatformMidi.c
# files needed for ports
LIBJSOUND_PORTFILES := \
PortMixerProvider.c \
PortMixer.c
# files needed for direct audio
LIBJSOUND_DAUDIOFILES := \
DirectAudioDeviceProvider.c \
DirectAudioDevice.c
ifeq ($(OPENJDK_TARGET_OS), windows)
EXTRA_SOUND_JNI_LIBS += jsoundds
LIBJSOUND_CFLAGS += -DX_PLATFORM=X_WINDOWS \
ifneq ($(OPENJDK_TARGET_OS), solaris)
LIBJSOUND_CFLAGS += \
-DUSE_PLATFORM_MIDI_OUT=TRUE \
-DUSE_PLATFORM_MIDI_IN=TRUE \
-DUSE_PORTS=TRUE
LIBJSOUND_SRC_FILES += \
PLATFORM_API_WinOS_Charset_Util.cpp \
PLATFORM_API_WinOS_MidiIn.cpp \
PLATFORM_API_WinOS_MidiOut.c \
PLATFORM_API_WinOS_Util.c \
PLATFORM_API_WinOS_Ports.c
LIBJSOUND_SRC_FILES += $(LIBJSOUND_MIDIFILES)
LIBJSOUND_SRC_FILES += $(LIBJSOUND_PORTFILES)
endif # OPENJDK_TARGET_OS windows
#
endif
ifeq ($(OPENJDK_TARGET_OS), windows)
LIBJSOUND_CFLAGS += -DX_PLATFORM=X_WINDOWS
endif
ifeq ($(OPENJDK_TARGET_OS), linux)
EXTRA_SOUND_JNI_LIBS += jsoundalsa
LIBJSOUND_CFLAGS += -DX_PLATFORM=X_LINUX
endif # OPENJDK_TARGET_OS linux
endif
ifeq ($(OPENJDK_TARGET_OS), aix)
LIBJSOUND_CFLAGS += -DX_PLATFORM=X_AIX
endif # OPENJDK_TARGET_OS aix
endif
ifeq ($(OPENJDK_TARGET_OS), macosx)
LIBJSOUND_TOOLCHAIN := TOOLCHAIN_LINK_CXX
LIBJSOUND_CFLAGS += -DX_PLATFORM=X_MACOSX \
-DUSE_PORTS=TRUE \
-DUSE_DAUDIO=TRUE \
-DUSE_PLATFORM_MIDI_OUT=TRUE \
-DUSE_PLATFORM_MIDI_IN=TRUE
LIBJSOUND_SRC_DIRS += $(TOPDIR)/src/java.desktop/macosx/native/libjsound
LIBJSOUND_SRC_FILES += \
PLATFORM_API_MacOSX_Utils.cpp \
PLATFORM_API_MacOSX_PCM.cpp \
PLATFORM_API_MacOSX_Ports.cpp \
PLATFORM_API_MacOSX_MidiIn.c \
PLATFORM_API_MacOSX_MidiOut.c \
PLATFORM_API_MacOSX_MidiUtils.c
LIBJSOUND_SRC_FILES += $(LIBJSOUND_MIDIFILES)
LIBJSOUND_SRC_FILES += $(LIBJSOUND_PORTFILES)
LIBJSOUND_SRC_FILES += $(LIBJSOUND_DAUDIOFILES)
endif # OPENJDK_TARGET_OS macosx
LIBJSOUND_CFLAGS += -DX_PLATFORM=X_MACOSX
endif
ifeq ($(OPENJDK_TARGET_OS), solaris)
LIBJSOUND_CFLAGS += -DX_PLATFORM=X_SOLARIS \
-DUSE_PORTS=TRUE \
-DUSE_DAUDIO=TRUE
LIBJSOUND_SRC_FILES += \
PLATFORM_API_SolarisOS_Utils.c \
PLATFORM_API_SolarisOS_Ports.c \
PLATFORM_API_SolarisOS_PCM.c
LIBJSOUND_SRC_FILES += $(LIBJSOUND_MIDIFILES)
LIBJSOUND_SRC_FILES += $(LIBJSOUND_PORTFILES)
LIBJSOUND_SRC_FILES += $(LIBJSOUND_DAUDIOFILES)
endif # OPENJDK_TARGET_OS solaris
LIBJSOUND_CFLAGS += -DEXTRA_SOUND_JNI_LIBS='"$(EXTRA_SOUND_JNI_LIBS)"'
LIBJSOUND_CFLAGS += -DX_PLATFORM=X_SOLARIS
endif
$(eval $(call SetupJdkLibrary, BUILD_LIBJSOUND, \
NAME := jsound, \
SRC := $(LIBJSOUND_SRC_DIRS), \
INCLUDE_FILES := $(LIBJSOUND_SRC_FILES), \
TOOLCHAIN := $(LIBJSOUND_TOOLCHAIN), \
OPTIMIZATION := LOW, \
CFLAGS := $(CFLAGS_JDKLIB) \
@ -127,10 +76,11 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJSOUND, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LIBS_unix := -ljava -ljvm, \
LIBS_linux := $(ALSA_LIBS), \
LIBS_macosx := -framework CoreAudio -framework CoreFoundation \
-framework CoreServices -framework AudioUnit $(LIBCXX) \
-framework CoreMIDI -framework AudioToolbox, \
LIBS_windows := $(WIN_JAVA_LIB) advapi32.lib winmm.lib, \
-framework CoreServices -framework AudioUnit \
-framework CoreMIDI -framework AudioToolbox $(LIBCXX), \
LIBS_windows := $(WIN_JAVA_LIB) advapi32.lib dsound.lib winmm.lib user32.lib ole32.lib, \
))
$(BUILD_LIBJSOUND): $(call FindLib, java.base, java)
@ -138,61 +88,3 @@ $(BUILD_LIBJSOUND): $(call FindLib, java.base, java)
TARGETS += $(BUILD_LIBJSOUND)
##########################################################################################
ifneq ($(filter jsoundalsa, $(EXTRA_SOUND_JNI_LIBS)), )
$(eval $(call SetupJdkLibrary, BUILD_LIBJSOUNDALSA, \
NAME := jsoundalsa, \
SRC := $(LIBJSOUND_SRC_DIRS), \
INCLUDE_FILES := Utilities.c $(LIBJSOUND_MIDIFILES) $(LIBJSOUND_PORTFILES) \
$(LIBJSOUND_DAUDIOFILES) \
PLATFORM_API_LinuxOS_ALSA_CommonUtils.c \
PLATFORM_API_LinuxOS_ALSA_PCM.c \
PLATFORM_API_LinuxOS_ALSA_PCMUtils.c \
PLATFORM_API_LinuxOS_ALSA_MidiIn.c \
PLATFORM_API_LinuxOS_ALSA_MidiOut.c \
PLATFORM_API_LinuxOS_ALSA_MidiUtils.c \
PLATFORM_API_LinuxOS_ALSA_Ports.c, \
OPTIMIZATION := LOW, \
CFLAGS := $(CFLAGS_JDKLIB) $(ALSA_CFLAGS) \
$(LIBJSOUND_CFLAGS) \
-DUSE_DAUDIO=TRUE \
-DUSE_PORTS=TRUE \
-DUSE_PLATFORM_MIDI_OUT=TRUE \
-DUSE_PLATFORM_MIDI_IN=TRUE, \
MAPFILE := $(TOPDIR)/make/mapfiles/libjsoundalsa/mapfile-vers, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LIBS := $(ALSA_LIBS) -ljava -ljvm, \
))
$(BUILD_LIBJSOUNDALSA): $(call FindLib, java.base, java)
TARGETS += $(BUILD_LIBJSOUNDALSA)
endif
##########################################################################################
ifneq ($(filter jsoundds, $(EXTRA_SOUND_JNI_LIBS)), )
$(eval $(call SetupJdkLibrary, BUILD_LIBJSOUNDDS, \
NAME := jsoundds, \
SRC := $(LIBJSOUND_SRC_DIRS), \
INCLUDE_FILES := Utilities.c $(LIBJSOUND_DAUDIOFILES) \
PLATFORM_API_WinOS_Charset_Util.cpp \
PLATFORM_API_WinOS_DirectSound.cpp, \
OPTIMIZATION := LOW, \
CFLAGS := $(CFLAGS_JDKLIB) \
$(LIBJSOUND_CFLAGS) \
-DUSE_DAUDIO=TRUE, \
LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LIBS := $(JDKLIB_LIBS) dsound.lib winmm.lib user32.lib ole32.lib, \
))
$(BUILD_LIBJSOUNDDS): $(call FindLib, java.base, java)
TARGETS += $(BUILD_LIBJSOUNDDS)
endif

View File

@ -65,8 +65,6 @@ SUNWprivate_1.1 {
Java_com_sun_media_sound_MidiOutDeviceProvider_nGetNumDevices;
Java_com_sun_media_sound_MidiOutDeviceProvider_nGetVendor;
Java_com_sun_media_sound_MidiOutDeviceProvider_nGetVersion;
Java_com_sun_media_sound_Platform_nGetExtraLibraries;
Java_com_sun_media_sound_Platform_nGetLibraryForFeature;
Java_com_sun_media_sound_Platform_nIsBigEndian;
Java_com_sun_media_sound_PortMixer_nClose;
Java_com_sun_media_sound_PortMixer_nControlGetFloatValue;

View File

@ -1,82 +0,0 @@
#
# Copyright (c) 2005, 2013, 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 library interface.
SUNWprivate_1.1 {
global:
Java_com_sun_media_sound_DirectAudioDeviceProvider_nGetNumDevices;
Java_com_sun_media_sound_DirectAudioDeviceProvider_nNewDirectAudioDeviceInfo;
Java_com_sun_media_sound_DirectAudioDevice_nAvailable;
Java_com_sun_media_sound_DirectAudioDevice_nClose;
Java_com_sun_media_sound_DirectAudioDevice_nFlush;
Java_com_sun_media_sound_DirectAudioDevice_nGetBufferSize;
Java_com_sun_media_sound_DirectAudioDevice_nGetBytePosition;
Java_com_sun_media_sound_DirectAudioDevice_nGetFormats;
Java_com_sun_media_sound_DirectAudioDevice_nIsStillDraining;
Java_com_sun_media_sound_DirectAudioDevice_nOpen;
Java_com_sun_media_sound_DirectAudioDevice_nRead;
Java_com_sun_media_sound_DirectAudioDevice_nRequiresServicing;
Java_com_sun_media_sound_DirectAudioDevice_nService;
Java_com_sun_media_sound_DirectAudioDevice_nSetBytePosition;
Java_com_sun_media_sound_DirectAudioDevice_nStart;
Java_com_sun_media_sound_DirectAudioDevice_nStop;
Java_com_sun_media_sound_DirectAudioDevice_nWrite;
Java_com_sun_media_sound_MidiInDeviceProvider_nGetDescription;
Java_com_sun_media_sound_MidiInDeviceProvider_nGetName;
Java_com_sun_media_sound_MidiInDeviceProvider_nGetNumDevices;
Java_com_sun_media_sound_MidiInDeviceProvider_nGetVendor;
Java_com_sun_media_sound_MidiInDeviceProvider_nGetVersion;
Java_com_sun_media_sound_MidiInDevice_nClose;
Java_com_sun_media_sound_MidiInDevice_nGetMessages;
Java_com_sun_media_sound_MidiInDevice_nGetTimeStamp;
Java_com_sun_media_sound_MidiInDevice_nOpen;
Java_com_sun_media_sound_MidiInDevice_nStart;
Java_com_sun_media_sound_MidiInDevice_nStop;
Java_com_sun_media_sound_MidiOutDeviceProvider_nGetDescription;
Java_com_sun_media_sound_MidiOutDeviceProvider_nGetName;
Java_com_sun_media_sound_MidiOutDeviceProvider_nGetNumDevices;
Java_com_sun_media_sound_MidiOutDeviceProvider_nGetVendor;
Java_com_sun_media_sound_MidiOutDeviceProvider_nGetVersion;
Java_com_sun_media_sound_MidiOutDevice_nClose;
Java_com_sun_media_sound_MidiOutDevice_nGetTimeStamp;
Java_com_sun_media_sound_MidiOutDevice_nOpen;
Java_com_sun_media_sound_MidiOutDevice_nSendLongMessage;
Java_com_sun_media_sound_MidiOutDevice_nSendShortMessage;
Java_com_sun_media_sound_PortMixerProvider_nGetNumDevices;
Java_com_sun_media_sound_PortMixerProvider_nNewPortMixerInfo;
Java_com_sun_media_sound_PortMixer_nClose;
Java_com_sun_media_sound_PortMixer_nControlGetFloatValue;
Java_com_sun_media_sound_PortMixer_nControlGetIntValue;
Java_com_sun_media_sound_PortMixer_nControlSetFloatValue;
Java_com_sun_media_sound_PortMixer_nControlSetIntValue;
Java_com_sun_media_sound_PortMixer_nGetControls;
Java_com_sun_media_sound_PortMixer_nGetPortCount;
Java_com_sun_media_sound_PortMixer_nGetPortName;
Java_com_sun_media_sound_PortMixer_nGetPortType;
Java_com_sun_media_sound_PortMixer_nOpen;
local:
*;
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2018, 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
@ -38,23 +38,9 @@ import java.util.StringTokenizer;
final class Platform {
// native library we need to load
private static final String libNameMain = "jsound";
private static final String libNameALSA = "jsoundalsa";
private static final String libNameDSound = "jsoundds";
private static final String libName = "jsound";
// extra libs handling: bit flags for each different library
public static final int LIB_MAIN = 1;
public static final int LIB_ALSA = 2;
public static final int LIB_DSOUND = 4;
// bit field of the constants above. Willbe set in loadLibraries
private static int loadedLibs = 0;
// features: the main native library jsound reports which feature is
// contained in which lib
public static final int FEATURE_MIDIIO = 1;
public static final int FEATURE_PORTS = 2;
public static final int FEATURE_DIRECT_AUDIO = 3;
private static boolean isNativeLibLoaded;
// SYSTEM CHARACTERISTICS
// vary according to hardware architecture
@ -66,7 +52,6 @@ final class Platform {
if(Printer.trace)Printer.trace(">> Platform.java: static");
loadLibraries();
readProperties();
}
/**
@ -95,72 +80,37 @@ final class Platform {
private static void loadLibraries() {
if(Printer.trace)Printer.trace(">>Platform.loadLibraries");
// load the main library
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
System.loadLibrary(libNameMain);
return null;
});
// just for the heck of it...
loadedLibs |= LIB_MAIN;
// now try to load extra libs. They are defined at compile time in the Makefile
// with the define EXTRA_SOUND_JNI_LIBS
String extraLibs = nGetExtraLibraries();
// the string is the libraries, separated by white space
StringTokenizer st = new StringTokenizer(extraLibs);
while (st.hasMoreTokens()) {
final String lib = st.nextToken();
try {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
System.loadLibrary(lib);
return null;
});
if (lib.equals(libNameALSA)) {
loadedLibs |= LIB_ALSA;
if (Printer.debug) Printer.debug("Loaded ALSA lib successfully.");
} else if (lib.equals(libNameDSound)) {
loadedLibs |= LIB_DSOUND;
if (Printer.debug) Printer.debug("Loaded DirectSound lib successfully.");
} else {
if (Printer.err) Printer.err("Loaded unknown lib '"+lib+"' successfully.");
}
} catch (Throwable t) {
if (Printer.err) Printer.err("Couldn't load library "+lib+": "+t.toString());
}
// load the native library
isNativeLibLoaded = true;
try {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
System.loadLibrary(libName);
return null;
});
} catch (Throwable t) {
if (Printer.err) Printer.err("Couldn't load library "+libName+": "+t.toString());
isNativeLibLoaded = false;
}
if (isNativeLibLoaded) {
bigEndian = nIsBigEndian();
}
}
static boolean isMidiIOEnabled() {
return isFeatureLibLoaded(FEATURE_MIDIIO);
if (Printer.debug) Printer.debug("Platform: Checking for MidiIO; library is loaded=" + isNativeLibLoaded);
return isNativeLibLoaded;
}
static boolean isPortsEnabled() {
return isFeatureLibLoaded(FEATURE_PORTS);
if (Printer.debug) Printer.debug("Platform: Checking for Ports; library is loaded=" + isNativeLibLoaded);
return isNativeLibLoaded;
}
static boolean isDirectAudioEnabled() {
return isFeatureLibLoaded(FEATURE_DIRECT_AUDIO);
if (Printer.debug) Printer.debug("Platform: Checking for DirectAudio; library is loaded=" + isNativeLibLoaded);
return isNativeLibLoaded;
}
private static boolean isFeatureLibLoaded(int feature) {
if (Printer.debug) Printer.debug("Platform: Checking for feature "+feature+"...");
int requiredLib = nGetLibraryForFeature(feature);
boolean isLoaded = (requiredLib != 0) && ((loadedLibs & requiredLib) == requiredLib);
if (Printer.debug) Printer.debug(" ...needs library "+requiredLib+". Result is loaded="+isLoaded);
return isLoaded;
}
// the following native methods are implemented in Platform.c
// the following native method is implemented in Platform.c
private static native boolean nIsBigEndian();
private static native String nGetExtraLibraries();
private static native int nGetLibraryForFeature(int feature);
/**
* Read the required system properties.
*/
private static void readProperties() {
// $$fb 2002-03-06: implement check for endianness in native. Facilitates porting !
bigEndian = nIsBigEndian();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2018, 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
@ -41,83 +41,3 @@ DEF_STATIC_JNI_OnLoad
JNIEXPORT jboolean JNICALL Java_com_sun_media_sound_Platform_nIsBigEndian(JNIEnv *env, jclass clss) {
return UTIL_IsBigEndianPlatform();
}
/*
* Class: com_sun_media_sound_Platform
* Method: nGetExtraLibraries
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_sun_media_sound_Platform_nGetExtraLibraries(JNIEnv *env, jclass clss) {
return (*env)->NewStringUTF(env, EXTRA_SOUND_JNI_LIBS);
}
/*
* Class: com_sun_media_sound_Platform
* Method: nGetLibraryForFeature
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_sun_media_sound_Platform_nGetLibraryForFeature
(JNIEnv *env, jclass clazz, jint feature) {
// for every OS
#if X_PLATFORM == X_WINDOWS
switch (feature) {
case com_sun_media_sound_Platform_FEATURE_MIDIIO:
return com_sun_media_sound_Platform_LIB_MAIN;
case com_sun_media_sound_Platform_FEATURE_PORTS:
return com_sun_media_sound_Platform_LIB_MAIN;
case com_sun_media_sound_Platform_FEATURE_DIRECT_AUDIO:
return com_sun_media_sound_Platform_LIB_DSOUND;
}
#endif
#if (X_PLATFORM == X_SOLARIS)
switch (feature) {
case com_sun_media_sound_Platform_FEATURE_MIDIIO:
return com_sun_media_sound_Platform_LIB_MAIN;
case com_sun_media_sound_Platform_FEATURE_PORTS:
return com_sun_media_sound_Platform_LIB_MAIN;
case com_sun_media_sound_Platform_FEATURE_DIRECT_AUDIO:
return com_sun_media_sound_Platform_LIB_MAIN;
}
#endif
#if (X_PLATFORM == X_LINUX)
switch (feature) {
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;
}
#endif
#if (X_PLATFORM == X_MACOSX)
switch (feature) {
case com_sun_media_sound_Platform_FEATURE_MIDIIO:
return com_sun_media_sound_Platform_LIB_MAIN;
case com_sun_media_sound_Platform_FEATURE_PORTS:
return com_sun_media_sound_Platform_LIB_MAIN;
case com_sun_media_sound_Platform_FEATURE_DIRECT_AUDIO:
return com_sun_media_sound_Platform_LIB_MAIN;
}
#endif
#if (X_PLATFORM == X_BSD)
switch (feature) {
case com_sun_media_sound_Platform_FEATURE_MIDIIO:
return com_sun_media_sound_Platform_LIB_MAIN;
#ifdef __FreeBSD__
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_PORTS:
return com_sun_media_sound_Platform_LIB_MAIN;
case com_sun_media_sound_Platform_FEATURE_DIRECT_AUDIO:
// 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;
}

View File

@ -1,182 +0,0 @@
/*
* Copyright (c) 2003, 2012, 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 */

View File

@ -1,82 +0,0 @@
/*
* Copyright (c) 2003, 2012, 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

View File

@ -1,354 +0,0 @@
/*
* Copyright (c) 2003, 2012, 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 */

View File

@ -1,179 +0,0 @@
/*
* Copyright (c) 2003, 2012, 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 */

View File

@ -1,481 +0,0 @@
/*
* Copyright (c) 2003, 2014, 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]='[';
// buffer[300] is enough to store the actual device string w/o overrun
getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_RAWMIDI);
strncat(buffer, "]", sizeof(buffer) - strlen(buffer) - 1);
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 */

View File

@ -1,85 +0,0 @@
/*
* Copyright (c) 2003, 2012, 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

View File

@ -1,941 +0,0 @@
/*
* Copyright (c) 2002, 2012, 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);
}
/** Workaround for cr 7033899, 7030629:
* dmix plugin doesn't like flush (snd_pcm_drop) when the buffer is empty
* (just opened, underruned or already flushed).
* Sometimes it causes PCM falls to -EBADFD error,
* sometimes causes bufferSize change.
* To prevent unnecessary flushes AlsaPcmInfo::isRunning & isFlushed are used.
*/
/* ******* 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;
short int isRunning; // see comment above
short int isFlushed; // see comment above
#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
if (channels <= 0) {
ERROR1("ERROR: Invalid number of channels=%d!\n", channels);
return NULL;
}
info = (AlsaPcmInfo*) malloc(sizeof(AlsaPcmInfo));
if (!info) {
ERROR0("Out of memory\n");
return NULL;
}
memset(info, 0, sizeof(AlsaPcmInfo));
// initial values are: stopped, flushed
info->isRunning = 0;
info->isFlushed = 1;
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);
if (ret) {
info->isRunning = 1;
// source line should keep isFlushed value until Write() is called;
// for target data line reset it right now.
if (!isSource) {
info->isFlushed = 0;
}
}
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;
}
info->isRunning = 0;
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);
if (writtenFrames > 0) {
// reset "flushed" flag
info->isFlushed = 0;
}
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;
}
if (!info->isRunning && info->isFlushed) {
// PCM has nothing to read
return 0;
}
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");
if (info->isFlushed) {
// nothing to drop
return 1;
}
ret = snd_pcm_drop(info->handle);
if (ret != 0) {
ERROR1("ERROR in snd_pcm_drop: %s\n", snd_strerror(ret));
return FALSE;
}
info->isFlushed = 1;
if (info->isRunning) {
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 (info->isFlushed || 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 (!info->isFlushed && 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

View File

@ -1,292 +0,0 @@
/*
* Copyright (c) 2003, 2014, 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]='[';
// buffer[300] is enough to store the actual device string w/o overrun
getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_PCM);
strncat(buffer, "]", sizeof(buffer) - strlen(buffer) - 1);
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 */

View File

@ -1,73 +0,0 @@
/*
* Copyright (c) 2003, 2012, 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

View File

@ -1,724 +0,0 @@
/*
* Copyright (c) 2003, 2016, 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;
memset(controls, 0, sizeof(controls));
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 split/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