Compare commits

...

No commits in common. "0.2.0" and "main" have entirely different histories.
0.2.0 ... main

48 changed files with 5518 additions and 8600 deletions

166
.clang-format Normal file
View File

@ -0,0 +1,166 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Allman
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
StatementAttributeLikeMacros:
- Q_EMIT
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: NoIndent
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 1000000
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
*.dll filter=lfs diff=lfs merge=lfs -text
*.so filter=lfs diff=lfs merge=lfs -text
*.so.0 filter=lfs diff=lfs merge=lfs -text

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.vscode
.vs
visualc/x64
build/
android/buildandroid/

143
CMakeLists.txt Normal file
View File

@ -0,0 +1,143 @@
# CMake Project for FAudioGMS
cmake_minimum_required(VERSION 2.8.12)
project(FAudioGMS C)
#Options
option(BUILD_SHARED_LIBS "Build shared library" ON)
SET(LIB_MAJOR_VERSION "0")
SET(LIB_MINOR_VERSION "2")
SET(LIB_REVISION "0")
SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
# Build Type
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
# By default, we use Release
message(STATUS "Setting build type to 'Release' as none was specified.")
set(CMAKE_BUILD_TYPE "Release" CACHE
STRING "Choose the type of build." FORCE
)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
STRINGS "Debug" "Release" "RelWithDebInfo"
)
endif()
# Platform Flags
if(APPLE)
set(CMAKE_MACOSX_RPATH ON)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9)
set(LOBJC "objc")
elseif(WIN32)
# "FAudioGMS.dll", not "libFAudioGMS.dll"
set(CMAKE_SHARED_LIBRARY_PREFIX "")
endif()
if(UNIX)
SET(BIN_RPATH "\$ORIGIN;\$ORIGIN/assets") #thanks yoyo games
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ${BIN_RPATH})
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
endif()
# Source lists
add_library(FAudioGMS
#Public header
src/FAudioGMS.h
#Source files
lib/dr_wav.h
src/FAudioGMS.c
)
# FAudio Source lists
add_library(FAudio STATIC
# Public Headers
lib/FAudio/include/F3DAudio.h
lib/FAudio/include/FACT3D.h
lib/FAudio/include/FACT.h
lib/FAudio/include/FAPOBase.h
lib/FAudio/include/FAPOFX.h
lib/FAudio/include/FAPO.h
lib/FAudio/include/FAudioFX.h
lib/FAudio/include/FAudio.h
# Internal Headers
lib/FAudio/src/FACT_internal.h
lib/FAudio/src/FAudio_internal.h
lib/FAudio/src/stb.h
lib/FAudio/src/stb_vorbis.h
# Source Files
lib/FAudio/src/F3DAudio.c
lib/FAudio/src/FACT3D.c
lib/FAudio/src/FACT.c
lib/FAudio/src/FACT_internal.c
lib/FAudio/src/FAPOBase.c
lib/FAudio/src/FAPOFX.c
lib/FAudio/src/FAPOFX_echo.c
lib/FAudio/src/FAPOFX_eq.c
lib/FAudio/src/FAPOFX_masteringlimiter.c
lib/FAudio/src/FAPOFX_reverb.c
lib/FAudio/src/FAudio.c
lib/FAudio/src/FAudioFX_reverb.c
lib/FAudio/src/FAudioFX_volumemeter.c
lib/FAudio/src/FAudio_internal.c
lib/FAudio/src/FAudio_internal_simd.c
lib/FAudio/src/FAudio_operationset.c
lib/FAudio/src/FAudio_platform_sdl2.c
lib/FAudio/src/FAudio_platform_win32.c
)
# Build flags
if(NOT MSVC)
set_property(TARGET FAudioGMS PROPERTY COMPILE_FLAGS "-std=gnu99 -Wall -Wno-strict-aliasing -pedantic")
set_property(TARGET FAudio PROPERTY COMPILE_FLAGS "-fPIC")
endif()
# includes
target_include_directories(FAudioGMS PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/FAudio/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/SDL/include>
)
target_include_directories(FAudio PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/FAudio/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/SDL/include>
)
# Soname
set_target_properties(FAudioGMS PROPERTIES OUTPUT_NAME "FAudioGMS"
VERSION ${LIB_VERSION}
SOVERSION ${LIB_MAJOR_VERSION}
)
# Internal Dependencies
target_link_libraries(FAudioGMS PRIVATE FAudio ${LOBJC})
# SDL2 Dependency
if (DEFINED SDL2_INCLUDE_DIRS AND DEFINED SDL2_LIBRARIES)
message(STATUS "using pre-defined SDL2 variables SDL2_INCLUDE_DIRS and SDL2_LIBRARIES")
target_include_directories(FAudioGMS PUBLIC "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_include_directories(FAudio PUBLIC "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_link_libraries(FAudioGMS PUBLIC ${SDL2_LIBRARIES})
else()
# Only try to autodetect if both SDL2 variables aren't explicitly set
find_package(SDL2 CONFIG)
if (TARGET SDL2::SDL2)
message(STATUS "using TARGET SDL2::SDL2")
target_link_libraries(FAudioGMS PUBLIC SDL2::SDL2)
target_link_libraries(FAudio PUBLIC SDL2::SDL2)
elseif (TARGET SDL2)
message(STATUS "using TARGET SDL2")
target_link_libraries(FAudioGMS PUBLIC SDL2)
target_link_libraries(FAudio PUBLIC SDL2)
else()
message(STATUS "no TARGET SDL2::SDL2, or SDL2, using variables")
target_include_directories(FAudioGMS PUBLIC "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_include_directories(FAudio PUBLIC "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_link_libraries(FAudioGMS PUBLIC ${SDL2_LIBRARIES})
endif()
endif()

View File

@ -10,21 +10,63 @@ FAudioGMS can leak memory if used improperly, so keep track of your sound instan
As a final addendum, if the YoYo Games circus needs a new clown for tasks like fixing the audio engine that's been mostly broken for a decade, I am a highly skilled programmer and I work for competitive rates. You know where to find me.
## Usage
If you are on Windows, make SURE "Use x64 Windows runtime" is checked under Game Options -> Windows!
Place your audio files in Included Files. The `.yymps` has an example structure.
Make sure the `AUDIO` object is created once at the start of your game.
You can use the documented wrapper functions included in the `.yymps` to control audio,
or you can just use the C API directly and wrap it yourself.
Detailed API documentation can be found [here](http://moonside.games/docs/FAudioGMS/#/latest/).
## Platforms
FAudio itself is cross-platform and has been deployed by hundreds of games across many platforms.
FAudioGMS intends to support all the platforms that FAudio does,
but I need to get around to personally building and testing it for these platforms.
I will not be supporting 32-bit Windows. Catch up to 2004 and switch to 64-bit.
### Tested
- Windows
- Windows (64-bit)
- Linux
- Android (I will not help you if it breaks, sorry, Android sucks, ask Nik if you need help)
### Theoretical
- OSX
- Linux
- Nintendo Switch
- Xbox One
- iOS
### Not Supported
- Windows (32-bit)
- HTML5 (if someone wants to try this go ahead but I'm not touching that garbage with a 10 foot pole)
## Dependencies
FAudioGMS depends on FAudio (obviously) and SDL2. We avoid directly depending on the C runtime.
## Building
### Windows
Open the project contained in the `visualc` directory in Visual Studio and build.
### OSX/Linux
```sh
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_C_COMPILER=/usr/bin/clang -S . -B ./build
cd ./build
make
```
### Console Platforms
You will need to show proof of NDA to obtain SDL source for your platform.
## License
This library is licensed under the zlib license. See LICENSE file for details.

69
android/Android.mk Normal file
View File

@ -0,0 +1,69 @@
# FAudioGMS Android.mk file
# PS: Expect hell
SAVED_LOCAL_PATH := $(call my-dir)
LOCAL_PATH := $(SAVED_LOCAL_PATH)
SDL_PATH := $(LOCAL_PATH)/../lib/SDL
FAUDIO_PATH := $(LOCAL_PATH)/../lib/FAudio
FAUDIOGMS_PATH := $(LOCAL_PATH)/..
# First we import SDL 2
include $(SDL_PATH)/Android.mk
# Then we compile FAudio as a static library
include $(CLEAR_VARS)
LOCAL_PATH := $(SAVED_LOCAL_PATH)
LOCAL_MODULE := FAudio_static
LOCAL_MODULE_FILENAME := libFAudio
LOCAL_SHARED_LIBRARIES := SDL2
LOCAL_C_INCLUDES := $(SDL_PATH)/include $(FAUDIO_PATH)/include $(FAUDIO_PATH)/src
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_LDLIBS :=
LOCAL_EXPORT_LDLIBS := -ldl -llog -landroid
LOCAL_SRC_FILES := \
$(FAUDIO_PATH)/src/F3DAudio.c \
$(FAUDIO_PATH)/src/FACT3D.c \
$(FAUDIO_PATH)/src/FACT.c \
$(FAUDIO_PATH)/src/FACT_internal.c \
$(FAUDIO_PATH)/src/FAPOBase.c \
$(FAUDIO_PATH)/src/FAPOFX.c \
$(FAUDIO_PATH)/src/FAPOFX_echo.c \
$(FAUDIO_PATH)/src/FAPOFX_eq.c \
$(FAUDIO_PATH)/src/FAPOFX_masteringlimiter.c \
$(FAUDIO_PATH)/src/FAPOFX_reverb.c \
$(FAUDIO_PATH)/src/FAudio.c \
$(FAUDIO_PATH)/src/FAudioFX_reverb.c \
$(FAUDIO_PATH)/src/FAudioFX_volumemeter.c \
$(FAUDIO_PATH)/src/FAudio_internal.c \
$(FAUDIO_PATH)/src/FAudio_internal_simd.c \
$(FAUDIO_PATH)/src/FAudio_operationset.c \
$(FAUDIO_PATH)/src/FAudio_platform_sdl2.c \
$(FAUDIO_PATH)/src/FAudio_platform_win32.c \
$(FAUDIO_PATH)/src/XNA_Song.c \
$(FAUDIO_PATH)/src/FAudio_gstreamer.c
include $(BUILD_STATIC_LIBRARY)
# And then we do our stuff...
include $(CLEAR_VARS)
LOCAL_PATH := $(SAVED_LOCAL_PATH)
LOCAL_MODULE := FAudioGMS
# Tell ndk-build we rely on these two fellas:
LOCAL_SHARED_LIBRARIES := SDL2 FAudio_static
LOCAL_C_INCLUDES := $(SDL_PATH)/include $(FAUDIO_PATH)/include $(FAUDIOGMS_PATH)/src
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := $(FAUDIOGMS_PATH)/src/FAudioGMS.c $(LOCAL_PATH)/FAudioGMS_JNI.c
include $(BUILD_SHARED_LIBRARY)

468
android/FAudioGMS_JNI.c Normal file
View File

@ -0,0 +1,468 @@
/* FAudioGMS - Game Maker FAudio bindings in C
*
* Copyright (c) 2021 Evan Hemsley
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Evan "cosmonaut" Hemsley <evan@moonside.games>
*
*/
/* These are the Native -> JNI conv wrappers, they must only be built for Android */
#ifdef __ANDROID__
/* no mangling please */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <jni.h>
#include <FAudioGMS.h>
/*
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1Init
JNIEXPORT: export this function for JNI
jdouble: return type, a GameMaker function must always return something
JNICALL: JNI calling convention
Java_class_path_here_classNameHere_Function_Name_Here
classpath: org.screwyoyo.faudiogms
classname: FAudioGMSNative
function name: FAudioGMSNative_FAudioGMS_1Init
underscores must be escaped with _1
*/
/* replace this with -1.0 or NAN if you wish... */
/* ideally, a jdouble should map to a double */
#define NOTHING ((jdouble)0.0)
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1Init
(JNIEnv* jniEnv, jclass jniThis, jdouble _spatialDistanceScale, jdouble _timestep)
{
FAudioGMS_Init(_spatialDistanceScale, _timestep);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1StaticSound_1LoadWAV
(JNIEnv* jniEnv, jclass jniThis, jstring _filePath)
{
jboolean isCopy;
const char* filePath;
jdouble ret;
filePath = (*jniEnv)->GetStringUTFChars(jniEnv, _filePath, &isCopy);
ret = FAudioGMS_StaticSound_LoadWAV((char *)filePath);
(*jniEnv)->ReleaseStringUTFChars(jniEnv, _filePath, filePath);
return ret;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1StaticSound_1CreateSoundInstance
(JNIEnv* jniEnv, jclass jniThis, jdouble _staticSoundID)
{
return (jdouble)FAudioGMS_StaticSound_CreateSoundInstance(_staticSoundID);
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1StaticSound_1Destroy
(JNIEnv* jniEnv, jclass jniThis, jdouble _staticSoundID)
{
FAudioGMS_StaticSound_Destroy(_staticSoundID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1StreamingSound_1LoadOGG
(JNIEnv* jniEnv, jclass jniThis, jstring _filepath, jdouble _bufferSizeInBytes)
{
jboolean isCopy;
const char* filepath;
jdouble ret;
filepath = (*jniEnv)->GetStringUTFChars(jniEnv, _filepath, &isCopy);
ret = FAudioGMS_StreamingSound_LoadOGG((char *)filepath, _bufferSizeInBytes);
(*jniEnv)->ReleaseStringUTFChars(jniEnv, _filepath, filepath);
return ret;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1Play
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
FAudioGMS_SoundInstance_Play(_soundInstanceID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1Pause
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
FAudioGMS_SoundInstance_Pause(_soundInstanceID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1Stop
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
FAudioGMS_SoundInstance_Stop(_soundInstanceID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1QueueSyncPlay
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
FAudioGMS_SoundInstance_QueueSyncPlay(_soundInstanceID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SyncPlay
(JNIEnv* jniEnv, jclass jniThis)
{
FAudioGMS_SoundInstance_SyncPlay();
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetPlayRegion
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _startInMilliseconds, jdouble _endInMilliseconds)
{
FAudioGMS_SoundInstance_SetPlayRegion(_soundInstanceID, _startInMilliseconds, _endInMilliseconds);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetLoop
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _loop)
{
FAudioGMS_SoundInstance_SetLoop(_soundInstanceID, _loop);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetPan
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _pan)
{
FAudioGMS_SoundInstance_SetPan(_soundInstanceID, _pan);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetPitch
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _pitch)
{
FAudioGMS_SoundInstance_SetPitch(_soundInstanceID, _pitch);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetVolume
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _volume)
{
FAudioGMS_SoundInstance_SetVolume(_soundInstanceID, _volume);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1Set3DPosition
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _x, jdouble _y, jdouble _z)
{
FAudioGMS_SoundInstance_Set3DPosition(_soundInstanceID, _x, _y, _z);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1Set3DVelocity
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _xVelocity, jdouble _yVelocity, jdouble _zVelocity)
{
FAudioGMS_SoundInstance_Set3DVelocity(_soundInstanceID, _xVelocity, _yVelocity, _zVelocity);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1Set3DOrientation
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _xFront, jdouble _yFront, jdouble _zFront, jdouble _xTop, jdouble _yTop, jdouble _zTop)
{
FAudioGMS_SoundInstance_Set3DOrientation(_soundInstanceID, _xFront, _yFront, _zFront, _xTop, _yTop, _zTop);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetTrackPositionInSeconds
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _trackPositionInSeconds)
{
FAudioGMS_SoundInstance_SetTrackPositionInSeconds(_soundInstanceID, _trackPositionInSeconds);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetVolumeOverTime
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _volume, jdouble _milliseconds)
{
FAudioGMS_SoundInstance_SetVolumeOverTime(_soundInstanceID, _volume, _milliseconds);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetLowPassFilter
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _lowPassFilter, jdouble _Q)
{
FAudioGMS_SoundInstance_SetLowPassFilter(_soundInstanceID, _lowPassFilter, _Q);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetHighPassFilter
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _highPassFilter, jdouble _Q)
{
FAudioGMS_SoundInstance_SetHighPassFilter(_soundInstanceID, _highPassFilter, _Q);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetBandPassFilter
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _bandPassFilter, jdouble _Q)
{
FAudioGMS_SoundInstance_SetBandPassFilter(_soundInstanceID, _bandPassFilter, _Q);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1QueueSoundInstance
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _queueSoundInstanceID)
{
FAudioGMS_SoundInstance_QueueSoundInstance(_soundInstanceID, _queueSoundInstanceID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1GetPitch
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
return (jdouble)FAudioGMS_SoundInstance_GetPitch(_soundInstanceID);
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1GetVolume
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
return (jdouble)FAudioGMS_SoundInstance_GetVolume(_soundInstanceID);
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1GetTrackLengthInSeconds
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
return (jdouble)FAudioGMS_SoundInstance_GetTrackLengthInSeconds(_soundInstanceID);
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1GetTrackPositionInSeconds
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
return (jdouble)FAudioGMS_SoundInstance_GetTrackPositionInSeconds(_soundInstanceID);
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1Destroy
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
FAudioGMS_SoundInstance_Destroy(_soundInstanceID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1DestroyWhenFinished
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID)
{
FAudioGMS_SoundInstance_DestroyWhenFinished(_soundInstanceID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1EffectChain_1Create
(JNIEnv* jniEnv, jclass jniThis)
{
return (jdouble)FAudioGMS_EffectChain_Create();
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1EffectChain_1AddDefaultReverb
(JNIEnv* jniEnv, jclass jniThis, jdouble _effectChainID)
{
FAudioGMS_EffectChain_AddDefaultReverb(_effectChainID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1EffectChain_1AddReverb
(JNIEnv* jniEnv, jclass jniThis,
jdouble _effectChainID,
jdouble _wetDryMix,
jdouble _reflectionsDelay,
jdouble _reverbDelay,
jdouble _earlyDiffusion,
jdouble _lateDiffusion,
jdouble _lowEQGain,
jdouble _lowEQCutoff,
jdouble _highEQGain,
jdouble _highEQCutoff,
jdouble _reflectionsGain,
jdouble _reverbGain,
jdouble _decayTime,
jdouble _density,
jdouble _roomSize)
{
FAudioGMS_EffectChain_AddReverb(
_effectChainID,
_wetDryMix,
_reflectionsDelay,
_reverbDelay,
_earlyDiffusion,
_lateDiffusion,
_lowEQGain,
_lowEQCutoff,
_highEQGain,
_highEQCutoff,
_reflectionsGain,
_reverbGain,
_decayTime,
_density,
_roomSize);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1EffectChain_1Destroy
(JNIEnv* jniEnv, jclass jniThis, jdouble _effectChainID)
{
FAudioGMS_EffectChain_Destroy(_effectChainID);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetEffectChain
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _effectChainID, jdouble _effectGain)
{
FAudioGMS_SoundInstance_SetEffectChain(_soundInstanceID, _effectChainID, _effectGain);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SoundInstance_1SetEffectGain
(JNIEnv* jniEnv, jclass jniThis, jdouble _soundInstanceID, jdouble _effectGain)
{
FAudioGMS_SoundInstance_SetEffectGain(_soundInstanceID, _effectGain);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SetMasteringEffectChain
(JNIEnv* jniEnv, jclass jniThis, jdouble _effectChainID, double _effectGain)
{
FAudioGMS_SetMasteringEffectChain(_effectChainID, _effectGain);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SetMasteringEffectGain
(JNIEnv* jniEnv, jclass jniThis, jdouble _effectGain)
{
FAudioGMS_SetMasteringEffectGain(_effectGain);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SetListenerPosition
(JNIEnv* jniEnv, jclass jniThis, jdouble _x, jdouble _y, jdouble _z)
{
FAudioGMS_SetListenerPosition(_x, _y, _z);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SetListenerVelocity
(JNIEnv* jniEnv, jclass jniThis, jdouble _xVelocity, jdouble _yVelocity, jdouble _zVelocity)
{
FAudioGMS_SetListenerVelocity(_xVelocity, _yVelocity, _zVelocity);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1SetListenerOrientation
(JNIEnv* jniEnv, jclass jniThis, jdouble _xFront, jdouble _yFront, jdouble _zFront, jdouble _xTop, jdouble _yTop, jdouble _zTop)
{
FAudioGMS_SetListenerOrientation(_xFront, _yFront, _zFront, _xTop, _yTop, _zTop);
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1PauseAll
(JNIEnv* jniEnv, jclass jniThis)
{
FAudioGMS_PauseAll();
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1ResumeAll
(JNIEnv* jniEnv, jclass jniThis)
{
FAudioGMS_ResumeAll();
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1StopAll
(JNIEnv* jniEnv, jclass jniThis)
{
FAudioGMS_StopAll();
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1Update
(JNIEnv* jniEnv, jclass jniThis)
{
FAudioGMS_Update();
return NOTHING;
}
JNIEXPORT jdouble JNICALL
Java_org_screwyoyo_faudiogms_FAudioGMSNative_FAudioGMS_1Destroy
(JNIEnv* jniEnv, jclass jniThis)
{
FAudioGMS_Destroy();
return NOTHING;
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __ANDROID__ */
/* Do nothing for other platforms, because they, thankly, do not require JNI bindings... */

8
android/build.ndk.cmd Normal file
View File

@ -0,0 +1,8 @@
cd /d %~dp0
mkdir buildandroid
call ndk-build NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=Android.mk APP_ABI="armeabi-v7a arm64-v8a x86 x86_64" APP_PLATFORM=android-16 APP_MODULES="SDL2 FAudio_static FAudioGMS" NDK_OUT=buildandroid\obj NDK_LIBS_OUT=buildandroid\lib
xcopy /e /r /y buildandroid\lib ..\gamemaker\extensions\FAudioGMS\AndroidSource\libs
rd /s /q buildandroid

24
android/build.ndk.sh Normal file
View File

@ -0,0 +1,24 @@
#!/bin/sh
cd `dirname $0`
# rm -rf buildandroid
mkdir -p buildandroid
# Make sure you have ndk-build and Android Sdk stuff in your $PATH!
ndk-build \
NDK_PROJECT_PATH=null \
APP_BUILD_SCRIPT=Android.mk \
APP_ABI="armeabi-v7a arm64-v8a x86 x86_64" \
APP_PLATFORM=android-16 \
APP_MODULES="SDL2 FAudio_static FAudioGMS" \
NDK_OUT=buildandroid/obj \
NDK_LIBS_OUT=buildandroid/lib
# Update gamemaker project folder..
cp -rf buildandroid/lib/* ../gamemaker/extensions/FAudioGMS/AndroidSource/libs
rm -rf buildandroid
# we're done here.

View File

@ -0,0 +1,142 @@
package ${YYAndroidPackageName}; /* this class will reside in Runner's package namespace */
import java.lang.String;
import android.util.Log;
import android.content.Intent;
import android.content.res.Configuration;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.app.Dialog;
import android.view.MotionEvent;
import org.screwyoyo.faudiogms.FAudioGMSNative;
import org.libsdl.app.SDLActivity;
import org.libsdl.app.SDL;
import org.libsdl.app.SDLAudioManager;
import com.yoyogames.runner.RunnerJNILib;
import android.content.res.AssetManager;
public class FAudioGMSBridge extends FAudioGMSNative implements IExtensionBase
{
public SDLActivity sdl;
public boolean handlenativepause; /* automatically pause all sounds on onPause or not? */
public boolean paused; /* are we currently paused */
public FAudioGMSBridge()
{
super();
handlenativepause = true; /* set this to false if you wish to handle pauses manually */
paused = false; /* this one must be false at initialization */
SDL.setContext(RunnerJNILib.GetApplicationContext());
sdl = new SDLActivity();
}
public void Init()
{
SDL.setContext(RunnerJNILib.GetApplicationContext());
sdl.onCreate(null);
}
public void onStart()
{
SDL.setContext(RunnerJNILib.GetApplicationContext());
sdl.onStart();
}
public void onRestart()
{
onStart();
}
public void onStop()
{
sdl.onStop();
}
public void onDestroy()
{
sdl.onDestroy();
}
public void onPause()
{
sdl.onPause();
if (handlenativepause && !paused)
{
paused = true;
FAudioGMS_PauseAll();
}
}
public void onResume()
{
sdl.onResume();
if (handlenativepause && paused)
{
paused = false;
FAudioGMS_ResumeAll();
}
}
public void onWindowFocusChanged(boolean hasFocus)
{
sdl.onWindowFocusChanged(hasFocus);
}
public void onConfigurationChanged(Configuration newConfig)
{
sdl.onConfigurationChanged(newConfig);
}
public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults)
{
sdl.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
public Dialog onCreateDialog(int id)
{
return null;
}
public boolean onTouchEvent(final MotionEvent event)
{
return false;
}
public boolean onGenericMotionEvent(MotionEvent event)
{
return false;
}
public boolean dispatchKeyEvent(KeyEvent event)
{
return false;
}
public boolean dispatchGenericMotionEvent(MotionEvent event)
{
return false;
}
public boolean performClick()
{
return false;
}
public void onNewIntent(android.content.Intent newIntent)
{
}
public void onActivityResult(int requestCode, int resultCode, Intent data){}
public boolean onKeyLongPress(int keyCode, KeyEvent event){return false;}
public boolean onCreateOptionsMenu( Menu menu ){return false;}
public boolean onOptionsItemSelected( MenuItem item ){return false;}
public boolean onKeyDown( int keyCode, KeyEvent event )
{ return false;}
public boolean onKeyUp( int keyCode, KeyEvent event ){return false;}
}

View File

@ -0,0 +1,15 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 28
}
repositories {
mavenCentral()
}
dependencies {
compile 'com.getkeepsafe.relinker:relinker:1.4.4'
}

View File

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.screwyoyo.faudiogms">
</manifest>

View File

@ -0,0 +1,22 @@
package org.libsdl.app;
import android.hardware.usb.UsbDevice;
interface HIDDevice
{
public int getId();
public int getVendorId();
public int getProductId();
public String getSerialNumber();
public int getVersion();
public String getManufacturerName();
public String getProductName();
public UsbDevice getDevice();
public boolean open();
public int sendFeatureReport(byte[] report);
public int sendOutputReport(byte[] report);
public boolean getFeatureReport(byte[] report);
public void setFrozen(boolean frozen);
public void close();
public void shutdown();
}

View File

@ -0,0 +1,649 @@
package org.libsdl.app;
import android.content.Context;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothGattService;
import android.hardware.usb.UsbDevice;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.os.*;
//import com.android.internal.util.HexDump;
import java.lang.Runnable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.UUID;
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
private static final String TAG = "hidapi";
private HIDDeviceManager mManager;
private BluetoothDevice mDevice;
private int mDeviceId;
private BluetoothGatt mGatt;
private boolean mIsRegistered = false;
private boolean mIsConnected = false;
private boolean mIsChromebook = false;
private boolean mIsReconnecting = false;
private boolean mFrozen = false;
private LinkedList<GattOperation> mOperations;
GattOperation mCurrentOperation = null;
private Handler mHandler;
private static final int TRANSPORT_AUTO = 0;
private static final int TRANSPORT_BREDR = 1;
private static final int TRANSPORT_LE = 2;
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
static class GattOperation {
private enum Operation {
CHR_READ,
CHR_WRITE,
ENABLE_NOTIFICATION
}
Operation mOp;
UUID mUuid;
byte[] mValue;
BluetoothGatt mGatt;
boolean mResult = true;
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mValue = value;
}
public void run() {
// This is executed in main thread
BluetoothGattCharacteristic chr;
switch (mOp) {
case CHR_READ:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
if (!mGatt.readCharacteristic(chr)) {
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case CHR_WRITE:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
chr.setValue(mValue);
if (!mGatt.writeCharacteristic(chr)) {
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case ENABLE_NOTIFICATION:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
if (chr != null) {
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
int properties = chr.getProperties();
byte[] value;
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
Log.e(TAG, "Unable to start notifications on input characteristic");
mResult = false;
return;
}
mGatt.setCharacteristicNotification(chr, true);
cccd.setValue(value);
if (!mGatt.writeDescriptor(cccd)) {
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
mResult = false;
return;
}
mResult = true;
}
}
}
}
public boolean finish() {
return mResult;
}
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null)
return null;
return valveService.getCharacteristic(uuid);
}
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.CHR_READ, uuid);
}
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
}
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
}
}
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
mManager = manager;
mDevice = device;
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
mIsRegistered = false;
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
mOperations = new LinkedList<GattOperation>();
mHandler = new Handler(Looper.getMainLooper());
mGatt = connectGatt();
// final HIDDeviceBLESteamController finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.checkConnectionForChromebookIssue();
// }
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
public String getIdentifier() {
return String.format("SteamController.%s", mDevice.getAddress());
}
public BluetoothGatt getGatt() {
return mGatt;
}
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
private BluetoothGatt connectGatt(boolean managed) {
if (Build.VERSION.SDK_INT >= 23) {
try {
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
} catch (Exception e) {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
} else {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
}
private BluetoothGatt connectGatt() {
return connectGatt(false);
}
protected int getConnectionState() {
Context context = mManager.getContext();
if (context == null) {
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
return BluetoothProfile.STATE_DISCONNECTED;
}
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
if (btManager == null) {
// This device doesn't support Bluetooth. We should never be here, because how did
// we instantiate a device to start with?
return BluetoothProfile.STATE_DISCONNECTED;
}
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
}
public void reconnect() {
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
mGatt.disconnect();
mGatt = connectGatt();
}
}
protected void checkConnectionForChromebookIssue() {
if (!mIsChromebook) {
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
// over and over.
return;
}
int connectionState = getConnectionState();
switch (connectionState) {
case BluetoothProfile.STATE_CONNECTED:
if (!mIsConnected) {
// We are in the Bad Chromebook Place. We can force a disconnect
// to try to recover.
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
else if (!isRegistered()) {
if (mGatt.getServices().size() > 0) {
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
probeService(this);
}
else {
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
}
else {
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
return;
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
case BluetoothProfile.STATE_CONNECTING:
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
break;
}
final HIDDeviceBLESteamController finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.checkConnectionForChromebookIssue();
}
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
private boolean isRegistered() {
return mIsRegistered;
}
private void setRegistered() {
mIsRegistered = true;
}
private boolean probeService(HIDDeviceBLESteamController controller) {
if (isRegistered()) {
return true;
}
if (!mIsConnected) {
return false;
}
Log.v(TAG, "probeService controller=" + controller);
for (BluetoothGattService service : mGatt.getServices()) {
if (service.getUuid().equals(steamControllerService)) {
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(inputCharacteristic)) {
Log.v(TAG, "Found input characteristic");
// Start notifications
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
enableNotification(chr.getUuid());
}
}
}
return true;
}
}
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
mIsConnected = false;
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
}
return false;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private void finishCurrentGattOperation() {
GattOperation op = null;
synchronized (mOperations) {
if (mCurrentOperation != null) {
op = mCurrentOperation;
mCurrentOperation = null;
}
}
if (op != null) {
boolean result = op.finish(); // TODO: Maybe in main thread as well?
// Our operation failed, let's add it back to the beginning of our queue.
if (!result) {
mOperations.addFirst(op);
}
}
executeNextGattOperation();
}
private void executeNextGattOperation() {
synchronized (mOperations) {
if (mCurrentOperation != null)
return;
if (mOperations.isEmpty())
return;
mCurrentOperation = mOperations.removeFirst();
}
// Run in main thread
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mOperations) {
if (mCurrentOperation == null) {
Log.e(TAG, "Current operation null in executor?");
return;
}
mCurrentOperation.run();
// now wait for the GATT callback and when it comes, finish this operation
}
}
});
}
private void queueGattOperation(GattOperation op) {
synchronized (mOperations) {
mOperations.add(op);
}
executeNextGattOperation();
}
private void enableNotification(UUID chrUuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
queueGattOperation(op);
}
public void writeCharacteristic(UUID uuid, byte[] value) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
queueGattOperation(op);
}
public void readCharacteristic(UUID uuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
queueGattOperation(op);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////// BluetoothGattCallback overridden methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
mIsReconnecting = false;
if (newState == 2) {
mIsConnected = true;
// Run directly, without GattOperation
if (!isRegistered()) {
mHandler.post(new Runnable() {
@Override
public void run() {
mGatt.discoverServices();
}
});
}
}
else if (newState == 0) {
mIsConnected = false;
}
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onServicesDiscovered status=" + status);
if (status == 0) {
if (gatt.getServices().size() == 0) {
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
mIsReconnecting = true;
mIsConnected = false;
gatt.disconnect();
mGatt = connectGatt(false);
}
else {
probeService(this);
}
}
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
}
finishCurrentGattOperation();
}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic)) {
// Only register controller with the native side once it has been fully configured
if (!isRegistered()) {
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
setRegistered();
}
}
finishCurrentGattOperation();
}
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// Enable this for verbose logging of controller input reports
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
}
}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//Log.v(TAG, "onDescriptorRead status=" + status);
}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
if (chr.getUuid().equals(inputCharacteristic)) {
boolean hasWrittenInputDescriptor = true;
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
if (reportChr != null) {
Log.v(TAG, "Writing report characteristic to enter valve mode");
reportChr.setValue(enterValveMode);
gatt.writeCharacteristic(reportChr);
}
}
finishCurrentGattOperation();
}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
}
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
//Log.v(TAG, "onReadRemoteRssi status=" + status);
}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
//Log.v(TAG, "onMtuChanged status=" + status);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////// Public API
//////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
// Valve Corporation
final int VALVE_USB_VID = 0x28DE;
return VALVE_USB_VID;
}
@Override
public int getProductId() {
// We don't have an easy way to query from the Bluetooth device, but we know what it is
final int D0G_BLE2_PID = 0x1106;
return D0G_BLE2_PID;
}
@Override
public String getSerialNumber() {
// This will be read later via feature report by Steam
return "12345";
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
return "Valve Corporation";
}
@Override
public String getProductName() {
return "Steam Controller";
}
@Override
public UsbDevice getDevice() {
return null;
}
@Override
public boolean open() {
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
// We need to skip the first byte, as that doesn't go over the air
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
writeCharacteristic(reportCharacteristic, actual_report);
return report.length;
}
@Override
public int sendOutputReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
writeCharacteristic(reportCharacteristic, report);
return report.length;
}
@Override
public boolean getFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return false;
}
//Log.v(TAG, "getFeatureReport");
readCharacteristic(reportCharacteristic);
return true;
}
@Override
public void close() {
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
@Override
public void shutdown() {
close();
BluetoothGatt g = mGatt;
if (g != null) {
g.disconnect();
g.close();
mGatt = null;
}
mManager = null;
mIsRegistered = false;
mIsConnected = false;
mOperations.clear();
}
}

View File

@ -0,0 +1,685 @@
package org.libsdl.app;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.os.Build;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.hardware.usb.*;
import android.os.Handler;
import android.os.Looper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class HIDDeviceManager {
private static final String TAG = "hidapi";
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
private static HIDDeviceManager sManager;
private static int sManagerRefCount = 0;
public static HIDDeviceManager acquire(Context context) {
if (sManagerRefCount == 0) {
sManager = new HIDDeviceManager(context);
}
++sManagerRefCount;
return sManager;
}
public static void release(HIDDeviceManager manager) {
if (manager == sManager) {
--sManagerRefCount;
if (sManagerRefCount == 0) {
sManager.close();
sManager = null;
}
}
}
private Context mContext;
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
private int mNextDeviceId = 0;
private SharedPreferences mSharedPreferences = null;
private boolean mIsChromebook = false;
private UsbManager mUsbManager;
private Handler mHandler;
private BluetoothManager mBluetoothManager;
private List<BluetoothDevice> mLastBluetoothDevices;
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceAttached(usbDevice);
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceDetached(usbDevice);
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
}
}
};
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// Bluetooth device was connected. If it was a Steam Controller, handle it
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device connected: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// Bluetooth device was disconnected, remove from controller manager (if any)
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device disconnected: " + device);
disconnectBluetoothDevice(device);
}
}
};
private HIDDeviceManager(final Context context) {
mContext = context;
// Make sure we have the HIDAPI library loaded with the native functions
try {
SDL.loadLibrary("hidapi");
} catch (Throwable e) {
Log.w(TAG, "Couldn't load hidapi: " + e.toString());
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(false);
builder.setTitle("SDL HIDAPI Error");
builder.setMessage("Please report the following error to the SDL maintainers: " + e.getMessage());
builder.setNegativeButton("Quit", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
// If our context is an activity, exit rather than crashing when we can't
// call our native functions.
Activity activity = (Activity)context;
activity.finish();
}
catch (ClassCastException cce) {
// Context wasn't an activity, there's nothing we can do. Give up and return.
}
}
});
builder.show();
return;
}
HIDDeviceRegisterCallback();
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
// if (shouldClear) {
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
// spedit.clear();
// spedit.commit();
// }
// else
{
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
}
initializeUSB();
initializeBluetooth();
}
public Context getContext() {
return mContext;
}
public int getDeviceIDForIdentifier(String identifier) {
SharedPreferences.Editor spedit = mSharedPreferences.edit();
int result = mSharedPreferences.getInt(identifier, 0);
if (result == 0) {
result = mNextDeviceId++;
spedit.putInt("next_device_id", mNextDeviceId);
}
spedit.putInt(identifier, result);
spedit.commit();
return result;
}
private void initializeUSB() {
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
/*
// Logging
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
Log.i(TAG,"Path: " + device.getDeviceName());
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
Log.i(TAG,"Product: " + device.getProductName());
Log.i(TAG,"ID: " + device.getDeviceId());
Log.i(TAG,"Class: " + device.getDeviceClass());
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
Log.i(TAG,"Vendor ID " + device.getVendorId());
Log.i(TAG,"Product ID: " + device.getProductId());
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
Log.i(TAG,"---------------------------------------");
// Get interface details
for (int index = 0; index < device.getInterfaceCount(); index++) {
UsbInterface mUsbInterface = device.getInterface(index);
Log.i(TAG," ***** *****");
Log.i(TAG," Interface index: " + index);
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
Log.i(TAG," ++++ ++++ ++++");
Log.i(TAG," Endpoint index: " + epi);
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
Log.i(TAG," Direction: " + mEndpoint.getDirection());
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
Log.i(TAG," Interval: " + mEndpoint.getInterval());
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
Log.i(TAG," Type: " + mEndpoint.getType());
}
}
}
Log.i(TAG," No more devices connected.");
*/
// Register for USB broadcasts and permission completions
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
mContext.registerReceiver(mUsbBroadcast, filter);
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
handleUsbDeviceAttached(usbDevice);
}
}
UsbManager getUSBManager() {
return mUsbManager;
}
private void shutdownUSB() {
try {
mContext.unregisterReceiver(mUsbBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
return true;
}
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
return true;
}
return false;
}
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB360_IFACE_SUBCLASS = 93;
final int XB360_IFACE_PROTOCOL = 1; // Wired
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
final int[] SUPPORTED_VENDORS = {
0x0079, // GPD Win 2
0x044f, // Thrustmaster
0x045e, // Microsoft
0x046d, // Logitech
0x056e, // Elecom
0x06a3, // Saitek
0x0738, // Mad Catz
0x07ff, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x1038, // SteelSeries
0x11c9, // Nacon
0x12ab, // Unknown
0x1430, // RedOctane
0x146b, // BigBen
0x1532, // Razer Sabertooth
0x15e4, // Numark
0x162e, // Joytech
0x1689, // Razer Onza
0x1949, // Lab126, Inc.
0x1bad, // Harmonix
0x24c6, // PowerA
};
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB1_IFACE_SUBCLASS = 71;
final int XB1_IFACE_PROTOCOL = 208;
final int[] SUPPORTED_VENDORS = {
0x045e, // Microsoft
0x0738, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x1532, // Razer Wildcat
0x24c6, // PowerA
0x2e24, // Hyperkin
};
if (usbInterface.getId() == 0 &&
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
connectHIDDeviceUSB(usbDevice);
}
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
List<Integer> devices = new ArrayList<Integer>();
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
devices.add(device.getId());
}
}
for (int id : devices) {
HIDDevice device = mDevicesById.get(id);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
boolean opened = false;
if (permission_granted) {
opened = device.open();
}
HIDDeviceOpenResult(device.getId(), opened);
}
}
}
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
synchronized (this) {
int interface_mask = 0;
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
// Check to see if we've already added this interface
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
int interface_id = usbInterface.getId();
if ((interface_mask & (1 << interface_id)) != 0) {
continue;
}
interface_mask |= (1 << interface_id);
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
int id = device.getId();
mDevicesById.put(id, device);
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
}
}
}
}
private void initializeBluetooth() {
Log.d(TAG, "Initializing Bluetooth");
if (mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
return;
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18)) {
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
return;
}
// Find bonded bluetooth controllers and create SteamControllers for them
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
// This device doesn't support Bluetooth.
return;
}
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
if (btAdapter == null) {
// This device has Bluetooth support in the codebase, but has no available adapters.
return;
}
// Get our bonded devices.
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
Log.d(TAG, "Bluetooth device available: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// NOTE: These don't work on Chromebooks, to my undying dismay.
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mContext.registerReceiver(mBluetoothBroadcast, filter);
if (mIsChromebook) {
mHandler = new Handler(Looper.getMainLooper());
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
// final HIDDeviceManager finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.chromebookConnectionHandler();
// }
// }, 5000);
}
}
private void shutdownBluetooth() {
try {
mContext.unregisterReceiver(mBluetoothBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
// This function provides a sort of dummy version of that, watching for changes in the
// connected devices and attempting to add controllers as things change.
public void chromebookConnectionHandler() {
if (!mIsChromebook) {
return;
}
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
for (BluetoothDevice bluetoothDevice : currentConnected) {
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
connected.add(bluetoothDevice);
}
}
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
if (!currentConnected.contains(bluetoothDevice)) {
disconnected.add(bluetoothDevice);
}
}
mLastBluetoothDevices = currentConnected;
for (BluetoothDevice bluetoothDevice : disconnected) {
disconnectBluetoothDevice(bluetoothDevice);
}
for (BluetoothDevice bluetoothDevice : connected) {
connectBluetoothDevice(bluetoothDevice);
}
final HIDDeviceManager finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.chromebookConnectionHandler();
}
}, 10000);
}
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
synchronized (this) {
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
device.reconnect();
return false;
}
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
int id = device.getId();
mBluetoothDevices.put(bluetoothDevice, device);
mDevicesById.put(id, device);
// The Steam Controller will mark itself connected once initialization is complete
}
return true;
}
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
synchronized (this) {
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
if (device == null)
return;
int id = device.getId();
mBluetoothDevices.remove(bluetoothDevice);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
if (bluetoothDevice == null) {
return false;
}
// If the device has no local name, we really don't want to try an equality check against it.
if (bluetoothDevice.getName() == null) {
return false;
}
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
}
private void close() {
shutdownUSB();
shutdownBluetooth();
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.shutdown();
}
mDevicesById.clear();
mBluetoothDevices.clear();
HIDDeviceReleaseCallback();
}
}
public void setFrozen(boolean frozen) {
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.setFrozen(frozen);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private HIDDevice getDevice(int id) {
synchronized (this) {
HIDDevice result = mDevicesById.get(id);
if (result == null) {
Log.v(TAG, "No device for id: " + id);
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
}
return result;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////// JNI interface functions
//////////////////////////////////////////////////////////////////////////////////////////////////////
public boolean openDevice(int deviceID) {
Log.v(TAG, "openDevice deviceID=" + deviceID);
HIDDevice device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
// Look to see if this is a USB device and we have permission to access it
UsbDevice usbDevice = device.getDevice();
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
HIDDeviceOpenPending(deviceID);
try {
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), 0));
} catch (Exception e) {
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
HIDDeviceOpenResult(deviceID, false);
}
return false;
}
try {
return device.open();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public int sendOutputReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendOutputReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public int sendFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public boolean getFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
return device.getFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public void closeDevice(int deviceID) {
try {
Log.v(TAG, "closeDevice deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return;
}
device.close();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////// Native methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
private native void HIDDeviceRegisterCallback();
private native void HIDDeviceReleaseCallback();
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
native void HIDDeviceOpenPending(int deviceID);
native void HIDDeviceOpenResult(int deviceID, boolean opened);
native void HIDDeviceDisconnected(int deviceID);
native void HIDDeviceInputReport(int deviceID, byte[] report);
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
}

View File

@ -0,0 +1,309 @@
package org.libsdl.app;
import android.hardware.usb.*;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
class HIDDeviceUSB implements HIDDevice {
private static final String TAG = "hidapi";
protected HIDDeviceManager mManager;
protected UsbDevice mDevice;
protected int mInterfaceIndex;
protected int mInterface;
protected int mDeviceId;
protected UsbDeviceConnection mConnection;
protected UsbEndpoint mInputEndpoint;
protected UsbEndpoint mOutputEndpoint;
protected InputThread mInputThread;
protected boolean mRunning;
protected boolean mFrozen;
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
mManager = manager;
mDevice = usbDevice;
mInterfaceIndex = interface_index;
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
mRunning = false;
}
public String getIdentifier() {
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
}
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
return mDevice.getVendorId();
}
@Override
public int getProductId() {
return mDevice.getProductId();
}
@Override
public String getSerialNumber() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
try {
result = mDevice.getSerialNumber();
}
catch (SecurityException exception) {
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
}
}
if (result == null) {
result = "";
}
return result;
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
result = mDevice.getManufacturerName();
}
if (result == null) {
result = String.format("%x", getVendorId());
}
return result;
}
@Override
public String getProductName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
result = mDevice.getProductName();
}
if (result == null) {
result = String.format("%x", getProductId());
}
return result;
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
public String getDeviceName() {
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
}
@Override
public boolean open() {
mConnection = mManager.getUSBManager().openDevice(mDevice);
if (mConnection == null) {
Log.w(TAG, "Unable to open USB device " + getDeviceName());
return false;
}
// Force claim our interface
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
if (!mConnection.claimInterface(iface, true)) {
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
close();
return false;
}
// Find the endpoints
for (int j = 0; j < iface.getEndpointCount(); j++) {
UsbEndpoint endpt = iface.getEndpoint(j);
switch (endpt.getDirection()) {
case UsbConstants.USB_DIR_IN:
if (mInputEndpoint == null) {
mInputEndpoint = endpt;
}
break;
case UsbConstants.USB_DIR_OUT:
if (mOutputEndpoint == null) {
mOutputEndpoint = endpt;
}
break;
}
}
// Make sure the required endpoints were present
if (mInputEndpoint == null || mOutputEndpoint == null) {
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
close();
return false;
}
// Start listening for input
mRunning = true;
mInputThread = new InputThread();
mInputThread.start();
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
0x09/*HID set_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
return -1;
}
if (skipped_report_id) {
++length;
}
return length;
}
@Override
public int sendOutputReport(byte[] report) {
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
if (r != report.length) {
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
}
return r;
}
@Override
public boolean getFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
/* Offset the return buffer by 1, so that the report ID
will remain in byte 0. */
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
0x01/*HID get_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
return false;
}
if (skipped_report_id) {
++res;
++length;
}
byte[] data;
if (res == length) {
data = report;
} else {
data = Arrays.copyOfRange(report, 0, res);
}
mManager.HIDDeviceFeatureReport(mDeviceId, data);
return true;
}
@Override
public void close() {
mRunning = false;
if (mInputThread != null) {
while (mInputThread.isAlive()) {
mInputThread.interrupt();
try {
mInputThread.join();
} catch (InterruptedException e) {
// Keep trying until we're done
}
}
mInputThread = null;
}
if (mConnection != null) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
mConnection.releaseInterface(iface);
mConnection.close();
mConnection = null;
}
}
@Override
public void shutdown() {
close();
mManager = null;
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
protected class InputThread extends Thread {
@Override
public void run() {
int packetSize = mInputEndpoint.getMaxPacketSize();
byte[] packet = new byte[packetSize];
while (mRunning) {
int r;
try
{
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
}
catch (Exception e)
{
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
break;
}
if (r < 0) {
// Could be a timeout or an I/O error
}
if (r > 0) {
byte[] data;
if (r == packetSize) {
data = packet;
} else {
data = Arrays.copyOfRange(packet, 0, r);
}
if (!mFrozen) {
mManager.HIDDeviceInputReport(mDeviceId, data);
}
}
}
}
}
}

View File

@ -0,0 +1,83 @@
package org.libsdl.app;
import android.content.Context;
import java.lang.Class;
import java.lang.reflect.Method;
/**
SDL library initialization
*/
public class SDL {
// This function should be called first and sets up the native code
// so it can call into the Java classes
public static void setupJNI() {
SDLActivity.nativeSetupJNI();
SDLAudioManager.nativeSetupJNI();
SDLControllerManager.nativeSetupJNI();
}
// This function should be called each time the activity is started
public static void initialize() {
SDLActivity.initialize();
SDLAudioManager.initialize();
SDLControllerManager.initialize();
}
// This function stores the current activity (SDL or not)
public static void setContext(Context context) {
mContext = context;
}
public static Context getContext() {
return mContext;
}
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
if (libraryName == null) {
throw new NullPointerException("No library name provided.");
}
try {
// Let's see if we have ReLinker available in the project. This is necessary for
// some projects that have huge numbers of local libraries bundled, and thus may
// trip a bug in Android's native library loader which ReLinker works around. (If
// loadLibrary works properly, ReLinker will simply use the normal Android method
// internally.)
//
// To use ReLinker, just add it as a dependency. For more information, see
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
//
Class<?> relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
Class<?> relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
Class<?> contextClass = mContext.getClassLoader().loadClass("android.content.Context");
Class<?> stringClass = mContext.getClassLoader().loadClass("java.lang.String");
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
// they've changed during updates.
Method forceMethod = relinkClass.getDeclaredMethod("force");
Object relinkInstance = forceMethod.invoke(null);
Class<?> relinkInstanceClass = relinkInstance.getClass();
// Actually load the library!
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
loadMethod.invoke(relinkInstance, mContext, libraryName, null, null);
}
catch (final Throwable e) {
// Fall back
try {
System.loadLibrary(libraryName);
}
catch (final UnsatisfiedLinkError ule) {
throw ule;
}
catch (final SecurityException se) {
throw se;
}
}
}
protected static Context mContext;
}

View File

@ -0,0 +1,616 @@
package org.libsdl.app;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.UiModeManager;
import android.content.ClipboardManager;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Hashtable;
import java.util.Locale;
/**
SDL Activity
*/
public class SDLActivity {
private static final String TAG = "yoyo";
public static boolean mIsResumedCalled, mHasFocus;
public static Locale mCurrentLocale;
// Handle the state of the native layer
public enum NativeState {
INIT, RESUMED, PAUSED
}
public static NativeState mNextNativeState;
public static NativeState mCurrentNativeState;
// Main components
public static SDLActivity mSingleton;
/**
* This method is called by SDL before loading the native shared libraries.
* It can be overridden to provide names of shared libraries to be loaded.
* The default implementation returns the defaults. It never returns null.
* An array returned by a new implementation must at least contain "SDL2".
* Also keep in mind that the order the libraries are loaded may matter.
* @return names of shared libraries to be loaded (e.g. "SDL2", "main").
*/
public String[] getLibraries() {
return new String[] {
"hidapi",
"SDL2",
"FAudioGMS"
};
}
// Load the .so
public void loadLibraries() {
for (String lib : getLibraries()) {
SDL.loadLibrary(lib);
}
}
public static void initialize() {
// The static nature of the singleton and Android quirkyness force us to initialize everything here
// Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
mSingleton = null;
mIsResumedCalled = false;
mHasFocus = true;
mNextNativeState = NativeState.INIT;
mCurrentNativeState = NativeState.INIT;
}
// Setup
public void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "Device: " + Build.DEVICE);
Log.v(TAG, "Model: " + Build.MODEL);
Log.v(TAG, "onCreate()");
try {
Thread.currentThread().setName("SDLActivity");
} catch (Exception e) {
Log.v(TAG, "modify thread properties failed " + e.toString());
}
// Load shared libraries
loadLibraries();
// Set up JNI
SDL.setupJNI();
// Initialize state
SDL.initialize();
// So we can call stuff from static callbacks
mSingleton = this;
}
public void pauseNativeThread() {
mNextNativeState = NativeState.PAUSED;
mIsResumedCalled = false;
SDLActivity.handleNativeState();
}
public void resumeNativeThread() {
mNextNativeState = NativeState.RESUMED;
mIsResumedCalled = true;
SDLActivity.handleNativeState();
}
// Events
public void onPause() {
Log.v(TAG, "onPause()");
pauseNativeThread();
}
public void onResume() {
Log.v(TAG, "onResume()");
resumeNativeThread();
}
public void onStop() {
Log.v(TAG, "onStop()");
pauseNativeThread();
}
public void onStart() {
Log.v(TAG, "onStart()");
resumeNativeThread();
}
public void onWindowFocusChanged(boolean hasFocus) {
Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
mHasFocus = hasFocus;
if (hasFocus) {
mNextNativeState = NativeState.RESUMED;
SDLActivity.handleNativeState();
nativeFocusChanged(true);
} else {
nativeFocusChanged(false);
mNextNativeState = NativeState.PAUSED;
SDLActivity.handleNativeState();
}
}
public void onLowMemory() {
Log.v(TAG, "onLowMemory()");
SDLActivity.nativeLowMemory();
}
public void onConfigurationChanged(Configuration newConfig) {
Log.v(TAG, "onConfigurationChanged()");
if (mCurrentLocale == null || !mCurrentLocale.equals(newConfig.locale)) {
mCurrentLocale = newConfig.locale;
SDLActivity.onNativeLocaleChanged();
}
}
public void onDestroy() {
Log.v(TAG, "onDestroy()");
SDLActivity.nativeSendQuit();
SDLActivity.nativeQuit();
}
// Called by JNI from SDL.
public static void manualBackButton() {
}
/* Transition to next state */
public static void handleNativeState() {
if (mNextNativeState == mCurrentNativeState) {
// Already in same state, discard.
return;
}
// Try a transition to init state
if (mNextNativeState == NativeState.INIT) {
mCurrentNativeState = mNextNativeState;
return;
}
// Try a transition to paused state
if (mNextNativeState == NativeState.PAUSED) {
nativePause();
mCurrentNativeState = mNextNativeState;
Log.i("yoyo", "SDL - PAUSE!");
return;
}
// Try a transition to resumed state
if (mNextNativeState == NativeState.RESUMED) {
nativeResume();
mCurrentNativeState = mNextNativeState;
Log.i("yoyo", "SDL - RESUME!");
return;
}
}
/**
* This method is called by SDL if SDL did not handle a message itself.
* This happens if a received message contains an unsupported command.
* Method can be overwritten to handle Messages in a different class.
* @param command the command of the message.
* @param param the parameter of the message. May be null.
* @return if the message was handled in overridden method.
*/
public boolean onUnhandledMessage(int command, Object param) {
return false;
}
/**
* A Handler class for Messages from native SDL applications.
* It uses current Activities as target (e.g. for the title).
* static to prevent implicit references to enclosing object.
*/
public static class SDLCommandHandler extends Handler {
@Override
public void handleMessage(Message msg) {
}
}
// Handler for the messages
Handler commandHandler = new SDLCommandHandler();
// Send a message from the SDLMain thread
boolean sendCommand(int command, Object data) {
Message msg = commandHandler.obtainMessage();
msg.arg1 = command;
msg.obj = data;
boolean result = commandHandler.sendMessage(msg);
return result;
}
// C functions we call
public static native int nativeSetupJNI();
public static native int nativeRunMain(String library, String function, Object arguments);
public static native void nativeLowMemory();
public static native void nativeSendQuit();
public static native void nativeQuit();
public static native void nativePause();
public static native void nativeResume();
public static native void nativeFocusChanged(boolean hasFocus);
public static native void onNativeDropFile(String filename);
public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float rate);
public static native void onNativeResize();
public static native void onNativeKeyDown(int keycode);
public static native void onNativeKeyUp(int keycode);
public static native boolean onNativeSoftReturnKey();
public static native void onNativeKeyboardFocusLost();
public static native void onNativeMouse(int button, int action, float x, float y, boolean relative);
public static native void onNativeTouch(int touchDevId, int pointerFingerId,
int action, float x,
float y, float p);
public static native void onNativeAccel(float x, float y, float z);
public static native void onNativeClipboardChanged();
public static native void onNativeSurfaceCreated();
public static native void onNativeSurfaceChanged();
public static native void onNativeSurfaceDestroyed();
public static native String nativeGetHint(String name);
public static native void nativeSetenv(String name, String value);
public static native void onNativeOrientationChanged(int orientation);
public static native void nativeAddTouch(int touchId, String name);
public static native void nativePermissionResult(int requestCode, boolean result);
public static native void onNativeLocaleChanged();
/**
* This method is called by SDL using JNI.
*/
public static boolean setActivityTitle(String title) {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static void setWindowStyle(boolean fullscreen) {
}
/**
* This method is called by SDL using JNI.
* This is a static method for JNI convenience, it calls a non-static method
* so that is can be overridden
*/
public static void setOrientation(int w, int h, boolean resizable, String hint){
}
/**
* This can be overridden
*/
public void setOrientationBis(int w, int h, boolean resizable, String hint){
}
/**
* This method is called by SDL using JNI.
*/
public static void minimizeWindow() {
}
/**
* This method is called by SDL using JNI.
*/
public static boolean shouldMinimizeOnFocusLoss() {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean isScreenKeyboardShown(){
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean supportsRelativeMouse(){
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean setRelativeMouseEnabled(boolean enabled) {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean sendMessage(int command, int param) {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static Context getContext() {
return SDL.getContext();
}
/**
* This method is called by SDL using JNI.
*/
public static boolean isAndroidTV() {
return false;
}
public static double getDiagonal() {
return 0.0;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean isTablet() {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean isChromebook() {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean isDeXMode() {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static DisplayMetrics getDisplayDPI() {
return getContext().getResources().getDisplayMetrics();
}
/**
* This method is called by SDL using JNI.
*/
public static boolean getManifestEnvironmentVariables() {
Log.i("yoyo", "sdl envvars req!");
nativeSetenv("SDL_ANDROID_BLOCK_ON_PAUSE", "1");
nativeSetenv("SDL_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO", "1");
Log.i("yoyo", "sdl envvars ok!");
return true;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean showTextInput(int x, int y, int w, int h) {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static Surface getNativeSurface() {
return null;
}
// Input
/**
* This method is called by SDL using JNI.
*/
public static void initTouch() {
}
// Messagebox
/** Result of current messagebox. Also used for blocking the calling thread. */
public final int[] messageboxSelection = new int[1];
/**
* This method is called by SDL using JNI.
* Shows the messagebox from UI thread and block calling thread.
* buttonFlags, buttonIds and buttonTexts must have same length.
* @param buttonFlags array containing flags for every button.
* @param buttonIds array containing id for every button.
* @param buttonTexts array containing text for every button.
* @param colors null for default or array of length 5 containing colors.
* @return button id or -1.
*/
public int messageboxShowMessageBox(
final int flags,
final String title,
final String message,
final int[] buttonFlags,
final int[] buttonIds,
final String[] buttonTexts,
final int[] colors) {
return -1;
}
public void messageboxCreateAndShow(Bundle args) {
}
private final Runnable rehideSystemUi = new Runnable() {
@Override
public void run() {
}
};
public void onSystemUiVisibilityChange(int visibility) {
}
/**
* This method is called by SDL using JNI.
*/
public static boolean clipboardHasText() {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static String clipboardGetText() {
return "";
}
/**
* This method is called by SDL using JNI.
*/
public static void clipboardSetText(String string) {
}
/**
* This method is called by SDL using JNI.
*/
public static int createCustomCursor(int[] colors, int width, int height, int hotSpotX, int hotSpotY) {
return 0;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean setCustomCursor(int cursorID) {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static boolean setSystemCursor(int cursorID) {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static void requestPermission(String permission, int requestCode) {
if (Build.VERSION.SDK_INT < 23) {
nativePermissionResult(requestCode, true);
return;
}
Activity activity = (Activity)getContext();
if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[]{permission}, requestCode);
} else {
nativePermissionResult(requestCode, true);
}
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
boolean result = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
nativePermissionResult(requestCode, result);
}
/**
* This method is called by SDL using JNI.
*/
public static int openURL(String url) {
return -1;
}
/**
* This method is called by SDL using JNI.
*/
public static int showToast(String message, int duration, int gravity, int xOffset, int yOffset) {
return -1;
}
}
/**
Simple runnable to start the SDL application
*/
class SDLMain implements Runnable {
@Override
public void run() {
try {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DISPLAY);
} catch (Exception e) {
Log.v("SDL", "modify thread properties failed " + e.toString());
}
}
}
class SDLInputConnection extends BaseInputConnection {
public SDLInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
}
public boolean sendKeyEvent(KeyEvent event) {
return false;
}
public boolean commitText(CharSequence text, int newCursorPosition) {
return false;
}
public boolean setComposingText(CharSequence text, int newCursorPosition) {
return false;
}
public static native void nativeCommitText(String text, int newCursorPosition);
public native void nativeGenerateScancodeForUnichar(char c);
public native void nativeSetComposingText(String text, int newCursorPosition);
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
return false;
}
}

View File

@ -0,0 +1,390 @@
package org.libsdl.app;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
public class SDLAudioManager
{
protected static final String TAG = "yoyo";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
}
// Audio
protected static String getAudioFormatString(int audioFormat) {
switch (audioFormat) {
case AudioFormat.ENCODING_PCM_8BIT:
return "8-bit";
case AudioFormat.ENCODING_PCM_16BIT:
return "16-bit";
case AudioFormat.ENCODING_PCM_FLOAT:
return "float";
default:
return Integer.toString(audioFormat);
}
}
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
int channelConfig;
int sampleSize;
int frameSize;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
/* On older devices let's use known good settings */
if (Build.VERSION.SDK_INT < 21) {
if (desiredChannels > 2) {
desiredChannels = 2;
}
if (sampleRate < 8000) {
sampleRate = 8000;
} else if (sampleRate > 48000) {
sampleRate = 48000;
}
}
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
int minSDKVersion = (isCapture ? 23 : 21);
if (Build.VERSION.SDK_INT < minSDKVersion) {
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
}
switch (audioFormat)
{
case AudioFormat.ENCODING_PCM_8BIT:
sampleSize = 1;
break;
case AudioFormat.ENCODING_PCM_16BIT:
sampleSize = 2;
break;
case AudioFormat.ENCODING_PCM_FLOAT:
sampleSize = 4;
break;
default:
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
sampleSize = 2;
break;
}
if (isCapture) {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_IN_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
}
} else {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
case 3:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 4:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
break;
case 5:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 6:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
case 7:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
break;
case 8:
if (Build.VERSION.SDK_INT >= 23) {
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
} else {
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
desiredChannels = 6;
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
}
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
}
/*
Log.v(TAG, "Speaker configuration (and order of channels):");
if ((channelConfig & 0x00000004) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
}
if ((channelConfig & 0x00000008) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
}
if ((channelConfig & 0x00000010) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
}
if ((channelConfig & 0x00000020) != 0) {
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
}
if ((channelConfig & 0x00000040) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
}
if ((channelConfig & 0x00000080) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
}
if ((channelConfig & 0x00000100) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
}
if ((channelConfig & 0x00000200) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
}
if ((channelConfig & 0x00000400) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
}
if ((channelConfig & 0x00000800) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
}
if ((channelConfig & 0x00001000) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
}
*/
}
frameSize = (sampleSize * desiredChannels);
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
int minBufferSize;
if (isCapture) {
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
} else {
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
}
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
int[] results = new int[4];
if (isCapture) {
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of AudioRecord");
mAudioRecord.release();
mAudioRecord = null;
return null;
}
mAudioRecord.startRecording();
}
results[0] = mAudioRecord.getSampleRate();
results[1] = mAudioRecord.getAudioFormat();
results[2] = mAudioRecord.getChannelCount();
} else {
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
/* Try again, with safer values */
Log.e(TAG, "Failed during initialization of Audio Track");
mAudioTrack.release();
mAudioTrack = null;
return null;
}
mAudioTrack.play();
}
results[0] = mAudioTrack.getSampleRate();
results[1] = mAudioTrack.getAudioFormat();
results[2] = mAudioTrack.getChannelCount();
}
results[3] = desiredFrames;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
return results;
}
/**
* This method is called by SDL using JNI.
*/
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteFloatBuffer(float[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(float)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteShortBuffer(short[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(short)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteByteBuffer(byte[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(byte)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
}
/** This method is called by SDL using JNI. */
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
/** This method is called by SDL using JNI. */
public static void captureClose() {
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
/** This method is called by SDL using JNI. */
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
try {
/* Set thread name */
if (iscapture) {
Thread.currentThread().setName("SDLAudioC" + device_id);
} else {
Thread.currentThread().setName("SDLAudioP" + device_id);
}
/* Set thread priority */
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
} catch (Exception e) {
Log.v(TAG, "modify thread properties failed " + e.toString());
}
}
public static native int nativeSetupJNI();
}

View File

@ -0,0 +1,92 @@
package org.libsdl.app;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
public class SDLControllerManager {
public static native int nativeSetupJNI();
public static native int nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id,
boolean is_accelerometer, int button_mask,
int naxes, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
float value);
public static native void onNativeHat(int device_id, int hat_id,
int x, int y);
private static final String TAG = "SDLControllerManager";
public static void initialize() {
}
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return false;
}
/**
* This method is called by SDL using JNI.
*/
public static void pollInputDevices() {
}
/**
* This method is called by SDL using JNI.
*/
public static void pollHapticDevices() {
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticRun(int device_id, float intensity, int length) {
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticStop(int device_id){
}
// Check if a given device is considered a possible SDL joystick
public static boolean isDeviceSDLJoystick(int deviceId) {
return false;
}
}
class SDLJoystickHandler {
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
public boolean handleMotionEvent(MotionEvent event) {
return false;
}
/**
* Handles adding and removing of input devices.
*/
public void pollInputDevices() {
}
}

View File

@ -0,0 +1,147 @@
package org.screwyoyo.faudiogms;
import java.lang.String;
public class FAudioGMSNative
{
public FAudioGMSNative()
{
super();
}
/* exactly as in FAudioGMS_JNI.c: */
public native double FAudioGMS_Init(double spatialDistanceScale, double timestep);
public native double FAudioGMS_StaticSound_LoadWAV(
String filePath); /* returns a static sound ID */
public native double FAudioGMS_StaticSound_CreateSoundInstance(
double staticSoundID); /* returns a sound instance ID */
public native double FAudioGMS_StaticSound_Destroy(double staticSoundID);
/* returns a sound instance ID */
public native double FAudioGMS_StreamingSound_LoadOGG(
String filepath,
double bufferSizeInBytes); /* if 0 is passed we will use a sensible default*/
public native double FAudioGMS_SoundInstance_Play(double soundInstanceID);
public native double FAudioGMS_SoundInstance_Pause(double soundInstanceID);
public native double FAudioGMS_SoundInstance_Stop(double soundInstanceID);
public native double FAudioGMS_SoundInstance_QueueSyncPlay(double soundInstanceID);
public native double FAudioGMS_SoundInstance_SyncPlay();
public native double FAudioGMS_SoundInstance_SetPlayRegion(
double soundInstanceID,
double startInMilliseconds,
double endInMilliseconds);
public native double FAudioGMS_SoundInstance_SetLoop(double soundInstanceID, double loop);
public native double FAudioGMS_SoundInstance_SetPan(double soundInstanceID, double pan);
public native double FAudioGMS_SoundInstance_SetPitch(double soundInstanceID, double pitch);
public native double FAudioGMS_SoundInstance_SetVolume(double soundInstanceID, double volume);
public native double FAudioGMS_SoundInstance_Set3DPosition(
double soundInstanceID,
double x,
double y,
double z);
public native double FAudioGMS_SoundInstance_Set3DVelocity(
double soundInstanceID,
double xVelocity,
double yVelocity,
double zVelocity);
public native double FAudioGMS_SoundInstance_Set3DOrientation(
double soundInstanceID,
double xFront,
double yFront,
double zFront,
double xTop,
double yTop,
double zTop);
public native double FAudioGMS_SoundInstance_SetTrackPositionInSeconds(
double soundInstanceID,
double trackPositionInSeconds);
public native double FAudioGMS_SoundInstance_SetVolumeOverTime(
double soundInstanceID,
double volume,
double milliseconds);
public native double FAudioGMS_SoundInstance_SetLowPassFilter(
double soundInstanceID,
double lowPassFilter,
double Q);
public native double FAudioGMS_SoundInstance_SetHighPassFilter(
double soundInstanceID,
double highPassFilter,
double Q);
public native double FAudioGMS_SoundInstance_SetBandPassFilter(
double soundInstanceID,
double bandPassFilter,
double Q);
public native double FAudioGMS_SoundInstance_QueueSoundInstance(
double soundInstanceID,
double queueSoundInstanceID);
public native double FAudioGMS_SoundInstance_GetPitch(double soundInstanceID);
public native double FAudioGMS_SoundInstance_GetVolume(double soundInstanceID);
public native double FAudioGMS_SoundInstance_GetTrackLengthInSeconds(double soundInstanceID);
public native double FAudioGMS_SoundInstance_GetTrackPositionInSeconds(double soundInstanceID);
public native double FAudioGMS_SoundInstance_Destroy(double soundInstanceID);
public native double FAudioGMS_SoundInstance_DestroyWhenFinished(double soundInstanceID);
public native double FAudioGMS_EffectChain_Create();
public native double FAudioGMS_EffectChain_AddDefaultReverb(double effectChainID);
public native double FAudioGMS_EffectChain_AddReverb(
double effectChainID,
double wetDryMix,
double reflectionsDelay,
double reverbDelay,
double earlyDiffusion,
double lateDiffusion,
double lowEQGain,
double lowEQCutoff,
double highEQGain,
double highEQCutoff,
double reflectionsGain,
double reverbGain,
double decayTime,
double density,
double roomSize);
public native double FAudioGMS_EffectChain_Destroy(double effectChainID);
/*
* NOTE: Any changes to the effect chain will NOT apply after this is set!
* You MUST call SetEffectChain again if you make changes to the effect
* chain parameters!
*/
public native double FAudioGMS_SoundInstance_SetEffectChain(
double soundInstanceID,
double effectChainID,
double effectGain);
public native double FAudioGMS_SoundInstance_SetEffectGain(
double soundInstanceID,
double effectGain);
public native double FAudioGMS_SetMasteringEffectChain(double effectChainID, double effectGain);
public native double FAudioGMS_SetMasteringEffectGain(double effectGain);
public native double FAudioGMS_SetListenerPosition(double x, double y, double z);
public native double FAudioGMS_SetListenerVelocity(
double xVelocity,
double yVelocity,
double zVelocity);
public native double FAudioGMS_SetListenerOrientation(
double xFront,
double yFront,
double zFront,
double xTop,
double yTop,
double zTop);
public native double FAudioGMS_PauseAll(); /* useful for mobile platforms, etc
*/
public native double FAudioGMS_ResumeAll(); /* same as above */
public native double FAudioGMS_StopAll();
public native double FAudioGMS_Update();
public native double FAudioGMS_Destroy();
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,7 +3,7 @@
"options": [],
"exportToGame": true,
"supportedTargets": -1,
"extensionVersion": "0.2.0",
"extensionVersion": "0.3.0",
"packageId": "",
"productId": "",
"author": "",
@ -13,7 +13,7 @@
"helpfile": "",
"iosProps": false,
"tvosProps": false,
"androidProps": false,
"androidProps": true,
"installdir": "",
"files": [
{"filename":"FAudioGMS.dll","origname":"","init":"","final":"","kind":1,"uncompress":false,"functions":[
@ -25,8 +25,7 @@
{"externalName":"FAudioGMS_StaticSound_LoadWAV","kind":1,"help":"FAudioGMS_StaticSound_LoadWAV(filePath)","hidden":false,"returnType":2,"argCount":0,"args":[
1,
],"resourceVersion":"1.0","name":"FAudioGMS_StaticSound_LoadWAV","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_Play","kind":1,"help":"FAudioGMS_SoundInstance_Play(id, loop)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
{"externalName":"FAudioGMS_SoundInstance_Play","kind":1,"help":"FAudioGMS_SoundInstance_Play(id)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_Play","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_Pause","kind":1,"help":"FAudioGMS_SoundInstance_Pause(id)","hidden":false,"returnType":2,"argCount":0,"args":[
@ -56,8 +55,9 @@
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SetListenerPosition","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_StreamingSound_LoadOGG","kind":1,"help":"FAudioGMS_StreamingSound_LoadOGG(filePath)","hidden":false,"returnType":2,"argCount":0,"args":[
{"externalName":"FAudioGMS_StreamingSound_LoadOGG","kind":1,"help":"FAudioGMS_StreamingSound_LoadOGG(filePath, bufferSizeInBytes)","hidden":false,"returnType":2,"argCount":0,"args":[
1,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_StreamingSound_LoadOGG","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_SetPan","kind":1,"help":"FAudioGMS_SoundInstance_SetPan(soundInstanceID, pan)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
@ -77,10 +77,10 @@
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_SetVolumeOverTime","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_StopAll","kind":1,"help":"FAudioGMS_StopAll()","hidden":false,"returnType":2,"argCount":0,"args":[],"resourceVersion":"1.0","name":"FAudioGMS_StopAll","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_SetTrackPosition","kind":1,"help":"FAudioGMS_SoundInstance_SetTrackPosition(soundInstanceID, trackPositionInSeconds)","hidden":false,"returnType":2,"argCount":0,"args":[
{"externalName":"FAudioGMS_SoundInstance_SetTrackPositionInSeconds","kind":1,"help":"FAudioGMS_SoundInstance_SetTrackPositionInSeconds(soundInstanceID, trackPositionInSeconds)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_SetTrackPosition","tags":[],"resourceType":"GMExtensionFunction",},
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_SetTrackPositionInSeconds","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_GetTrackLengthInSeconds","kind":1,"help":"FAudioGMS_SoundInstance_GetTrackLengthInSeconds(soundInstanceID)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_GetTrackLengthInSeconds","tags":[],"resourceType":"GMExtensionFunction",},
@ -155,7 +155,54 @@
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SetListenerVelocity","tags":[],"resourceType":"GMExtensionFunction",},
],"constants":[],"ProxyFiles":[],"copyToTargets":64,"order":[
{"externalName":"FAudioGMS_PauseAll","kind":1,"help":"FAudioGMS_PauseAll()","hidden":false,"returnType":2,"argCount":0,"args":[],"resourceVersion":"1.0","name":"FAudioGMS_PauseAll","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_ResumeAll","kind":1,"help":"FAudioGMS_ResumeAll()","hidden":false,"returnType":2,"argCount":0,"args":[],"resourceVersion":"1.0","name":"FAudioGMS_ResumeAll","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_SetPlayRegion","kind":1,"help":"FAudioGMS_SoundInstance_SetPlayRegion(soundInstanceID, startInMilliseconds, endInMilliseconds)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_SetPlayRegion","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SetMasteringEffectChain","kind":1,"help":"FAudioGMS_SetMasteringEffectChain(effectChainID, effectGain)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SetMasteringEffectChain","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SetMasteringEffectGain","kind":1,"help":"FAudioGMS_SetMasteringEffectGain(effectGain)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SetMasteringEffectGain","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_SetLoop","kind":1,"help":"FAudioGMS_SoundInstance_SetLoop(soundInstanceID, loop)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_SetLoop","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_QueueSoundInstance","kind":1,"help":"FAudioGMS_SoundInstance_QueueSoundInstance(soundInstanceID, queueSoundInstanceID)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_QueueSoundInstance","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_QueueSyncPlay","kind":1,"help":"FAudioGMS_SoundInstance_QueueSyncPlay(soundInstanceID)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_QueueSyncPlay","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_SyncPlay","kind":1,"help":"FAudioGMS_SoundInstance_SyncPlay()","hidden":false,"returnType":2,"argCount":0,"args":[],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_SyncPlay","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SoundInstance_Set3DOrientation","kind":1,"help":"FAudioGMS_SoundInstance_Set3DOrientation(soundInstanceID, xFront, yFront, zFront, xTop, yTop, zTop)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
2,
2,
2,
2,
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SoundInstance_Set3DOrientation","tags":[],"resourceType":"GMExtensionFunction",},
{"externalName":"FAudioGMS_SetListenerOrientation","kind":1,"help":"FAudioGMS_SetListenerOrientation(xFront, yFront, zFront, xTop, yTop, zTop)","hidden":false,"returnType":2,"argCount":0,"args":[
2,
2,
2,
2,
2,
2,
],"resourceVersion":"1.0","name":"FAudioGMS_SetListenerOrientation","tags":[],"resourceType":"GMExtensionFunction",},
],"constants":[],"ProxyFiles":[
{"TargetMask":7,"resourceVersion":"1.0","name":"libFAudioGMS.so","tags":[],"resourceType":"GMProxyFile",},
{"TargetMask":3,"resourceVersion":"1.0","name":"FAudioGMSAndroidDummy.ext","tags":[],"resourceType":"GMProxyFile",},
{"TargetMask":7,"resourceVersion":"1.0","name":"libSDL2-2.0.so.0","tags":[],"resourceType":"GMProxyFile",},
],"copyToTargets":200,"order":[
{"name":"FAudioGMS_Init","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_StaticSound_LoadWAV","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_StaticSound_CreateSoundInstance","path":"extensions/FAudioGMS/FAudioGMS.yy",},
@ -164,12 +211,17 @@
{"name":"FAudioGMS_SoundInstance_Play","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_Pause","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_Stop","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_QueueSyncPlay","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SyncPlay","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetLoop","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetPan","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetPitch","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetVolume","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_Set3DPosition","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_Set3DVelocity","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetTrackPosition","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_Set3DOrientation","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetTrackPositionInSeconds","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetPlayRegion","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetVolumeOverTime","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetLowPassFilter","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetHighPassFilter","path":"extensions/FAudioGMS/FAudioGMS.yy",},
@ -178,6 +230,7 @@
{"name":"FAudioGMS_SoundInstance_GetVolume","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_GetTrackLengthInSeconds","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_GetTrackPositionInSeconds","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_QueueSoundInstance","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_Destroy","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_DestroyWhenFinished","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_EffectChain_Create","path":"extensions/FAudioGMS/FAudioGMS.yy",},
@ -186,8 +239,13 @@
{"name":"FAudioGMS_EffectChain_Destroy","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetEffectChain","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SoundInstance_SetEffectGain","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SetMasteringEffectChain","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SetMasteringEffectGain","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SetListenerPosition","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SetListenerVelocity","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_SetListenerOrientation","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_ResumeAll","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_PauseAll","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_StopAll","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_Update","path":"extensions/FAudioGMS/FAudioGMS.yy",},
{"name":"FAudioGMS_Destroy","path":"extensions/FAudioGMS/FAudioGMS.yy",},
@ -197,7 +255,7 @@
"tvosclassname": null,
"tvosdelegatename": null,
"iosdelegatename": "",
"androidclassname": "",
"androidclassname": "FAudioGMSBridge",
"sourcedir": "",
"androidsourcedir": "",
"macsourcedir": "",
@ -221,7 +279,7 @@
"tvosThirdPartyFrameworkEntries": [],
"IncludedResources": [],
"androidPermissions": [],
"copyToTargets": 64,
"copyToTargets": 200,
"iosCocoaPods": "",
"tvosCocoaPods": "",
"iosCocoaPodDependencies": "",

BIN
gamemaker/extensions/FAudioGMS/libFAudioGMS.so (Stored with Git LFS) Executable file

Binary file not shown.

BIN
gamemaker/extensions/FAudioGMS/libSDL2-2.0.so.0 (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1 +1,17 @@
/// @description Clean up all resources when the game is about to end.
/* Destroy sound instances first: */
// sndInst.Stop();
// sndInst.Destroy();
// sndInst = undefined;
/* Only then destroy static sounds: */
// snd.Destroy();
// snd = undefined;
/* And only then, destroy effect chains */
effChain.Destroy();
effChain = undefined;
/* Finish up the system */
FAudioGMS_Destroy();

View File

@ -1,6 +1,15 @@
/// @description Initialize audio.
/* First initialize the system: */
var spatialDistanceScale = 50; // makes "3D" audio louder
FAudioGMS_Init(spatialDistanceScale, 1 / 60);
var timestep = game_get_speed(gamespeed_microseconds) / 1000000; // default, autodetect from GM timestep.
FAudioGMS_Init(spatialDistanceScale, timestep);
/* Init Effects Chains Here */
effChain = new EffectChain();
effChain.AddDefaultReverb();
/* Load Audio Assets Here */
// snd = LoadStaticSound("go-go-go-tigerblood.wav");
// sndInst = snd.Play();
// sndInst.SetEffectChain(effChain, 1); // apply default reverb to the instance.

View File

@ -1 +1,2 @@
FAudioGMS_Update();
/// @description MUST be called once per frame!
FAudioGMS_Update();

View File

@ -1,9 +1,16 @@
// working_directory on android is "assets/" which makes SDL freak out.
function GetPathPrepend()
{
if (os_type != os_android) return working_directory;
else return "";
}
// StaticSounds are usually short and intended to be played multiple times.
// All of the sound data lives in memory for as long as the StaticSound exists.
// Playing a StaticSound returns a SoundInstance.
function LoadStaticSound(filename)
{
var filePath = "audio\\static\\" + filename;
var filePath = GetPathPrepend() + "audio/static/" + filename;
var staticSoundID = FAudioGMS_StaticSound_LoadWAV(filePath);
return new StaticSound(staticSoundID);
}
@ -11,20 +18,33 @@ function LoadStaticSound(filename)
function StaticSound(_staticSoundID) constructor
{
staticSoundID = _staticSoundID;
// Returns a sound instance!
// MUST be destroyed when you aren't referencing it any more or you will leak memory!
static Play = function(pan = 0, pitch = 1, volume = 1, loop = false)
// Create a sound instance from this static sound.
static CreateSoundInstance = function()
{
var instanceID = FAudioGMS_StaticSound_CreateSoundInstance(staticSoundID);
var instance = new SoundInstance(instanceID);
return instance;
}
// Plays and returns a sound instance!
// MUST be destroyed when you aren't referencing it any more or you will leak memory!
static Play = function(pan = 0, pitch = 1, volume = 1, loop = false, loopStartInMilliseconds = 0, loopEndInMilliseconds = 0)
{
var instanceID = FAudioGMS_StaticSound_CreateSoundInstance(staticSoundID);
var instance = new SoundInstance(instanceID);
instance.SetLoop(loop);
if (loop)
{
instance.SetPlayRegion(loopStartInMilliseconds, loopEndInMilliseconds);
}
instance.SetPan(pan);
instance.SetPitch(pitch);
instance.SetVolume(volume);
instance.Play(loop);
instance.Play();
return instance;
}
// Automatically destroys the SoundInstance when playback is finished.
// Does NOT return an instance!
static PlayOneOff = function(pan = 0, pitch = 1, volume = 1)
@ -32,20 +52,21 @@ function StaticSound(_staticSoundID) constructor
var instance = Play(pan, pitch, volume, false);
instance.DestroyWhenFinished();
}
// Returns a sound instance!
// Plays and returns a sound instance!
// MUST be destroyed when you aren't referencing it any more or you will leak memory!
static PlaySpatial = function(xPosition, yPosition, zPosition, pitch = 1, volume = 1, loop = false)
{
var instanceID = FAudioGMS_StaticSound_CreateSoundInstance(staticSoundID);
var instance = new SoundInstance(instanceID);
instance.SetLoop(loop);
instance.Set3DPosition(xPosition, yPosition, zPosition);
instance.SetPitch(pitch);
instance.SetVolume(volume);
instance.Play(loop);
return instance;
}
// Automatically destroys the SoundInstance when playback is finished.
// Does NOT return an instance!
static PlaySpatialOneOff = function(xPosition, yPosition, zPosition, pitch = 1, volume = 1)
@ -59,7 +80,7 @@ function StaticSound(_staticSoundID) constructor
// If you call this while there are still instances of the StaticSound, you are going to have a bad time.
static Destroy = function()
{
FAudioGMS_StaticSound_Destroy(staticSoundID);
FAudioGMS_StaticSound_Destroy(staticSoundID);
}
}
@ -67,10 +88,10 @@ function StaticSound(_staticSoundID) constructor
// The audio is streamed off the disk, so only a small amount of memory is used at a time.
// Good for things like music or voiceover playback.
// Note that StreamingSounds are SoundInstances.
function LoadStreamingSound(filename)
function LoadStreamingSound(filename, bufferSizeInBytes = 0)
{
var filePath = "audio\\streaming\\" + filename;
soundInstanceID = FAudioGMS_StreamingSound_LoadOGG(filePath);
var filePath = GetPathPrepend() + "audio/streaming/" + filename;
soundInstanceID = FAudioGMS_StreamingSound_LoadOGG(filePath, bufferSizeInBytes);
return new SoundInstance(soundInstanceID);
}
@ -79,48 +100,60 @@ function SoundInstance(_soundInstanceID) constructor
soundInstanceID = _soundInstanceID;
// Plays the sound or resumes from pause.
static Play = function(loop = false)
static Play = function()
{
FAudioGMS_SoundInstance_Play(soundInstanceID, loop);
FAudioGMS_SoundInstance_Play(soundInstanceID);
}
// Pauses playback.
static Pause = function()
{
FAudioGMS_SoundInstance_Pause(soundInstanceID);
}
// Stops playback completely. If Play is called it will resume from the beginning of the sound.
static Stop = function()
{
FAudioGMS_SoundInstance_Stop(soundInstanceID);
FAudioGMS_SoundInstance_Stop(soundInstanceID);
}
// Sets the 3-dimensional position of the sound. You probably want to use SetListenerPosition too.
static Set3DPosition = function(xPosition, yPosition, zPosition)
{
FAudioGMS_SoundInstance_Set3DPosition(soundInstanceID, xPosition, yPosition, zPosition);
FAudioGMS_SoundInstance_Set3DPosition(soundInstanceID, xPosition, yPosition, zPosition);
}
// Sets the 3-dimensional velocity of the sound. You probably want to use SetListenerVelocity too.
static Set3DVelocity = function(xVelocity, yVelocity, zVelocity)
{
FAudioGMS_SoundInstance_Set3DVelocity(soundInstanceID, xVelocity, yVelocity, zVelocity);
FAudioGMS_SoundInstance_Set3DVelocity(soundInstanceID, xVelocity, yVelocity, zVelocity);
}
// Sets the 3-dimensional orientation of the sound.
static Set3DOrientation = function(xFront, yFront, zFront, xTop, yTop, zTop)
{
FAudioGMS_SoundInstance_Set3DOrientation(soundInstanceID, xFront, yFront, zFront, xTop, yTop, zTop);
}
// Sets whether the sound instance loops (true) or does not (false).
static SetLoop = function(loop)
{
FAudioGMS_SoundInstance_SetLoop(soundInstanceID, loop);
}
// Sets the panning value of the sound. -1 is farthest left, 1 is farthest right, 0 is center.
// NOTE: This is ignored if you have called Set3DPosition.
static SetPan = function(pan)
{
FAudioGMS_SoundInstance_SetPan(soundInstanceID, pan);
FAudioGMS_SoundInstance_SetPan(soundInstanceID, pan);
}
// Sets the pitch of the sound. Default is 1. Lower than 1 is pitched down, higher than 1 is pitched up.
static SetPitch = function(pitch)
{
FAudioGMS_SoundInstance_SetPitch(soundInstanceID, pitch);
FAudioGMS_SoundInstance_SetPitch(soundInstanceID, pitch);
}
// Sets the volume of the sound. Default is 1. Lower than 1 is quieter, greater than 1 is louder.
// If you set the milliseconds value then this will fade over time.
static SetVolume = function(volume, milliseconds = 0)
@ -131,16 +164,28 @@ function SoundInstance(_soundInstanceID) constructor
}
else
{
FAudioGMS_SoundInstance_SetVolume(soundInstanceID, volume);
FAudioGMS_SoundInstance_SetVolume(soundInstanceID, volume);
}
}
// Sets the position of track playback.
// Sets the position of track playback.
static SetTrackPosition = function(seconds)
{
FAudioGMS_SoundInstance_SetTrackPosition(soundInstanceID, seconds);
FAudioGMS_SoundInstance_SetTrackPositionInSeconds(soundInstanceID, seconds);
}
// Queues this sound instance for playing in sync.
static QueueSyncPlay = function()
{
FAudioGMS_SoundInstance_QueueSyncPlay(soundInstanceID);
}
// Sets the playback region for the sound instance.
static SetPlayRegion = function(loopStartInMilliseconds, loopEndInMilliseconds)
{
FAudioGMS_SoundInstance_SetPlayRegion(soundInstanceID, loopStartInMilliseconds, loopEndInMilliseconds);
}
// Sets a low pass filter on the sound.
// frequency: 0.0 <-> 1.0. 1.0 means all sound gets through.
// Q: set this to 1 unless you know what you're doing
@ -148,7 +193,7 @@ function SoundInstance(_soundInstanceID) constructor
{
FAudioGMS_SoundInstance_SetLowPassFilter(soundInstanceID, frequency, Q);
}
// Sets a high pass filter on the sound.
// frequency: 0.0 <-> 1.0. 0.0 means all sound gets through.
// Q: set this to 1 unless you know what you're doing
@ -156,7 +201,7 @@ function SoundInstance(_soundInstanceID) constructor
{
FAudioGMS_SoundInstance_SetHighPassFilter(soundInstanceID, frequency, Q);
}
// Sets a band pass filter ont he sound.
// frequency: 0.0 <-> 1.0
// Q: set this to 1 unless you know what you're doing
@ -164,7 +209,7 @@ function SoundInstance(_soundInstanceID) constructor
{
FAudioGMS_SoundInstance_SetBandPassFilter(soundInstanceID, frequency, Q);
}
// Sets an effect chain on the sound.
// Gain is how much the effect chain "affects" the sound. 1.0 is max.
// NOTE: Any changes to the effect chain will NOT apply automatically after this is set!
@ -173,64 +218,75 @@ function SoundInstance(_soundInstanceID) constructor
{
FAudioGMS_SoundInstance_SetEffectChain(soundInstanceID, effectChain.effectChainID, gain);
}
// Sets the effect gain of the current effect chain.
// Does nothing if no effect chain is set.
static SetEffectGain = function(gain)
{
FAudioGMS_SoundInstance_SetEffectGain(soundInstanceID, gain);
}
static QueueSoundInstance = function(queueSoundInstance)
{
FAudioGMS_SoundInstance_QueueSoundInstance(soundInstanceID, queueSoundInstance.soundInstanceID);
}
// Gets the pitch of the sound.
static GetPitch = function()
{
return FAudioGMS_SoundInstance_GetPitch(soundInstanceID);
return FAudioGMS_SoundInstance_GetPitch(soundInstanceID);
}
// Gets the volume of the sound.
static GetVolume = function()
{
return FAudioGMS_SoundInstance_GetVolume(soundInstanceID);
return FAudioGMS_SoundInstance_GetVolume(soundInstanceID);
}
// Gets the total length of the track.
static GetTrackLengthInSeconds = function()
{
return FAudioGMS_SoundInstance_GetTrackLengthInSeconds(soundInstanceID);
return FAudioGMS_SoundInstance_GetTrackLengthInSeconds(soundInstanceID);
}
// Gets the current track position.
static GetTrackPositionInSeconds = function()
{
return FAudioGMS_SoundInstance_GetTrackPositionInSeconds(soundInstanceID);
}
// Destroys the FAudioGMS sound instance.
// If you use the SoundInstance after calling this you are going to have a bad time.
static Destroy = function()
{
FAudioGMS_SoundInstance_Destroy(soundInstanceID);
FAudioGMS_SoundInstance_Destroy(soundInstanceID);
}
// Sets the FAudioGMS sound instance to destroy itself when playback is done.
// Calling this on a looping sound is not a good idea.
// If you use the SoundInstance after calling this you might have a bad time.
static DestroyWhenFinished = function()
{
FAudioGMS_SoundInstance_DestroyWhenFinished(soundInstanceID);
FAudioGMS_SoundInstance_DestroyWhenFinished(soundInstanceID);
}
SetPan(0);
SetPitch(1);
SetVolume(1);
}
// Plays the sound instance queue.
function SyncPlay()
{
FAudioGMS_SoundInstance_SyncPlay();
}
// Effect chains allow you to modify sound playback using audio effects.
// Right now only reverb is implemented, but more effects will probably come later.
function EffectChain() constructor
{
effectChainID = FAudioGMS_EffectChain_Create();
// Adds a reverb effect to the effect chain.
static AddReverb = function(
wetDryMix,
@ -266,7 +322,7 @@ function EffectChain() constructor
roomSize
);
}
// Adds a default reverb effect to the effect chain.
// This is a good place to start if you don't know what all the reverb params do.
static AddDefaultReverb = function()
@ -289,12 +345,12 @@ function EffectChain() constructor
100
);
}
// Destroys the FAudioGMS effect chain.
// If you use the EffectChain after calling this you are going to have a bad time.
static Destroy = function()
{
FAudioGMS_EffectChain_Destroy(effectChainID);
FAudioGMS_EffectChain_Destroy(effectChainID);
}
}
@ -307,11 +363,17 @@ function SetListenerPosition(xPosition, yPosition, zPosition)
// Sets the velocity of the listener for 3D audio.
function SetListenerVelocity(xVelocity, yVelocity, zVelocity)
{
FAudioGMS_SetListenerVelocity(xVelocity, yVelocity, zVelocity);
FAudioGMS_SetListenerVelocity(xVelocity, yVelocity, zVelocity);
}
// Stops all audio playback.
// Sets the orientation of the listener for 3D audio.
function SetListenerOrientation(xFront, yFront, zFront, xTop, yTop, zTop)
{
FAudioGMS_SetListenerOrientation(xFront, yFront, zFront, xTop, yTop, zTop);
}
// Stops all audio playback.
function StopAllAudio()
{
FAudioGMS_StopAll();
FAudioGMS_StopAll();
}

@ -1 +1 @@
Subproject commit 28528bc885581bad58050517d78848cb206b2aa3
Subproject commit caf9754455b8ad0f6df5d024203940e9b6e3f47e

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -36,75 +36,144 @@
#endif
#ifdef __cplusplus
extern "C" {
extern "C"
{
#endif /* __cplusplus */
FAUDIOGMSAPI void FAudioGMS_Init(double spatialDistanceScale, double timestep);
FAUDIOGMSAPI void FAudioGMS_Init(double spatialDistanceScale, double timestep);
FAUDIOGMSAPI double FAudioGMS_StaticSound_LoadWAV(char *filePath); /* returns a static sound ID */
FAUDIOGMSAPI double FAudioGMS_StaticSound_CreateSoundInstance(double staticSoundID); /* returns a sound instance ID */
FAUDIOGMSAPI void FAudioGMS_StaticSound_Destroy(double staticSoundID);
FAUDIOGMSAPI double FAudioGMS_StaticSound_LoadWAV(
char *filePath); /* returns a static sound ID */
FAUDIOGMSAPI double FAudioGMS_StaticSound_CreateSoundInstance(
double staticSoundID); /* returns a sound instance ID */
FAUDIOGMSAPI void FAudioGMS_StaticSound_Destroy(double staticSoundID);
/* returns a sound instance ID */
FAUDIOGMSAPI double FAudioGMS_StreamingSound_LoadOGG(
char *filepath,
double bufferSizeInBytes); /* if 0 is passed we will use a sensible default*/
FAUDIOGMSAPI double FAudioGMS_StreamingSound_LoadOGG(char* filepath); /* returns a sound instance ID */
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Play(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Pause(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Stop(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Play(double soundInstanceID, double loop);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Pause(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Stop(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_QueueSyncPlay(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SyncPlay();
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetPan(double soundInstanceID, double pan);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetPitch(double soundInstanceID, double pitch);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetVolume(double soundInstanceID, double volume);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Set3DPosition(double soundInstanceID, double x, double y, double z);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Set3DVelocity(double soundInstanceID, double xVelocity, double yVelocity, double zVelocity);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetTrackPositionInSeconds(double soundInstanceID, double trackPositionInSeconds);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetVolumeOverTime(double soundInstanceID, double volume, double milliseconds);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetLowPassFilter(double soundInstanceID, double lowPassFilter, double Q);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetHighPassFilter(double soundInstanceID, double highPassFilter, double Q);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetBandPassFilter(double soundInstanceID, double bandPassFilter, double Q);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetPlayRegion(
double soundInstanceID,
double startInMilliseconds,
double endInMilliseconds);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetLoop(double soundInstanceID, double loop);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetPan(double soundInstanceID, double pan);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetPitch(double soundInstanceID, double pitch);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetVolume(double soundInstanceID, double volume);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Set3DPosition(
double soundInstanceID,
double x,
double y,
double z);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Set3DVelocity(
double soundInstanceID,
double xVelocity,
double yVelocity,
double zVelocity);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Set3DOrientation(
double soundInstanceID,
double xFront,
double yFront,
double zFront,
double xTop,
double yTop,
double zTop);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetTrackPositionInSeconds(
double soundInstanceID,
double trackPositionInSeconds);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetVolumeOverTime(
double soundInstanceID,
double volume,
double milliseconds);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetLowPassFilter(
double soundInstanceID,
double lowPassFilter,
double Q);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetHighPassFilter(
double soundInstanceID,
double highPassFilter,
double Q);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetBandPassFilter(
double soundInstanceID,
double bandPassFilter,
double Q);
FAUDIOGMSAPI double FAudioGMS_SoundInstance_GetPitch(double soundInstanceID);
FAUDIOGMSAPI double FAudioGMS_SoundInstance_GetVolume(double soundInstanceID);
FAUDIOGMSAPI double FAudioGMS_SoundInstance_GetTrackLengthInSeconds(double soundInstanceID);
FAUDIOGMSAPI double FAudioGMS_SoundInstance_GetTrackPositionInSeconds(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_QueueSoundInstance(
double soundInstanceID,
double queueSoundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Destroy(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_DestroyWhenFinished(double soundInstanceID);
FAUDIOGMSAPI double FAudioGMS_SoundInstance_GetPitch(double soundInstanceID);
FAUDIOGMSAPI double FAudioGMS_SoundInstance_GetVolume(double soundInstanceID);
FAUDIOGMSAPI double FAudioGMS_SoundInstance_GetTrackLengthInSeconds(double soundInstanceID);
FAUDIOGMSAPI double FAudioGMS_SoundInstance_GetTrackPositionInSeconds(double soundInstanceID);
FAUDIOGMSAPI double FAudioGMS_EffectChain_Create();
FAUDIOGMSAPI void FAudioGMS_EffectChain_AddDefaultReverb(double effectChainID);
FAUDIOGMSAPI void FAudioGMS_EffectChain_AddReverb(
double effectChainID,
double wetDryMix,
double reflectionsDelay,
double reverbDelay,
double earlyDiffusion,
double lateDiffusion,
double lowEQGain,
double lowEQCutoff,
double highEQGain,
double highEQCutoff,
double reflectionsGain,
double reverbGain,
double decayTime,
double density,
double roomSize
);
FAUDIOGMSAPI void FAudioGMS_EffectChain_Destroy(double effectChainID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_Destroy(double soundInstanceID);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_DestroyWhenFinished(double soundInstanceID);
/*
* NOTE: Any changes to the effect chain will NOT apply after this is set!
* You MUST call SetEffectChain again if you make changes to the effect chain parameters!
*/
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetEffectChain(double soundInstanceID, double effectChainID, double effectGain);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetEffectGain(double soundInstanceID, double effectGain);
FAUDIOGMSAPI double FAudioGMS_EffectChain_Create();
FAUDIOGMSAPI void FAudioGMS_EffectChain_AddDefaultReverb(double effectChainID);
FAUDIOGMSAPI void FAudioGMS_EffectChain_AddReverb(
double effectChainID,
double wetDryMix,
double reflectionsDelay,
double reverbDelay,
double earlyDiffusion,
double lateDiffusion,
double lowEQGain,
double lowEQCutoff,
double highEQGain,
double highEQCutoff,
double reflectionsGain,
double reverbGain,
double decayTime,
double density,
double roomSize);
FAUDIOGMSAPI void FAudioGMS_EffectChain_Destroy(double effectChainID);
FAUDIOGMSAPI void FAudioGMS_SetListenerPosition(double x, double y, double z);
FAUDIOGMSAPI void FAudioGMS_SetListenerVelocity(double xVelocity, double yVelocity, double zVelocity);
/*
* NOTE: Any changes to the effect chain will NOT apply after this is set!
* You MUST call SetEffectChain again if you make changes to the effect
* chain parameters!
*/
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetEffectChain(
double soundInstanceID,
double effectChainID,
double effectGain);
FAUDIOGMSAPI void FAudioGMS_SoundInstance_SetEffectGain(
double soundInstanceID,
double effectGain);
FAUDIOGMSAPI void FAudioGMS_StopAll();
FAUDIOGMSAPI void FAudioGMS_SetMasteringEffectChain(double effectChainID, double effectGain);
FAUDIOGMSAPI void FAudioGMS_SetMasteringEffectGain(double effectGain);
FAUDIOGMSAPI void FAudioGMS_Update();
FAUDIOGMSAPI void FAudioGMS_Destroy();
FAUDIOGMSAPI void FAudioGMS_SetListenerPosition(double x, double y, double z);
FAUDIOGMSAPI void FAudioGMS_SetListenerVelocity(
double xVelocity,
double yVelocity,
double zVelocity);
FAUDIOGMSAPI void FAudioGMS_SetListenerOrientation(
double xFront,
double yFront,
double zFront,
double xTop,
double yTop,
double zTop);
FAUDIOGMSAPI void FAudioGMS_PauseAll(); /* useful for mobile platforms, etc
*/
FAUDIOGMSAPI void FAudioGMS_ResumeAll(); /* same as above */
FAUDIOGMSAPI void FAudioGMS_StopAll();
FAUDIOGMSAPI void FAudioGMS_Update();
FAUDIOGMSAPI void FAudioGMS_Destroy();
#ifdef __cplusplus
}

View File

@ -39,13 +39,14 @@
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PlatformToolset>ClangCL</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@ -89,20 +90,23 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">SetupAPI.lib;Version.lib;Winmm.lib;Imm32.lib;libucrt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">libucrt.lib;SetupAPI.lib;Version.lib;Winmm.lib;Imm32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ProjectReference>
<UseLibraryDependencyInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</UseLibraryDependencyInputs>
</ProjectReference>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\FAudioGMS.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\dr_wav.h" />
<ClInclude Include="..\src\FAudioGMS.h" />
</ItemGroup>
<ItemGroup>