mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-30 16:01:51 +00:00
Add Android audio drivers based on OpenSLES and Oboe (#464)
This set of changes implements audio drivers for Android, OpenSLES and Oboe. The changes in the original sources are kept minimal so that it should be easily maintained.
This commit is contained in:
parent
eb0c8eab9f
commit
daa037b0d3
20 changed files with 1118 additions and 7 deletions
|
@ -55,6 +55,7 @@ option ( enable-fpe-check "enable Floating Point Exception checks and debug mess
|
|||
option ( enable-portaudio "compile PortAudio support" off )
|
||||
option ( enable-profiling "profile the dsp code" off )
|
||||
option ( enable-trap-on-fpe "enable SIGFPE trap on Floating Point Exceptions" off )
|
||||
option ( enable-ubsan "compile and link against UBSan (for debugging fluidsynth internals)" off )
|
||||
|
||||
# Options enabled by default
|
||||
option ( enable-aufile "compile support for sound file output" on )
|
||||
|
@ -65,6 +66,8 @@ option ( enable-jack "compile JACK support (if it is available)" on )
|
|||
option ( enable-ladspa "enable LADSPA effect units" on )
|
||||
option ( enable-libsndfile "compile libsndfile support (if it is available)" on )
|
||||
option ( enable-midishare "compile MidiShare support (if it is available)" on )
|
||||
option ( enable-opensles "compile OpenSLES support (if it is available)" off )
|
||||
option ( enable-oboe "compile Oboe support (requires OpenSLES and/or AAudio)" off )
|
||||
option ( enable-network "enable network support (requires BSD sockets)" on )
|
||||
option ( enable-oss "compile OSS support (if it is available)" on )
|
||||
option ( enable-dsound "compile DirectSound support (if it is available)" on )
|
||||
|
@ -169,6 +172,7 @@ unset ( LIBFLUID_CPPFLAGS CACHE )
|
|||
unset ( LIBFLUID_LIBS CACHE )
|
||||
unset ( FLUID_CPPFLAGS CACHE )
|
||||
unset ( FLUID_LIBS CACHE )
|
||||
unset ( ENABLE_UBSAN CACHE )
|
||||
|
||||
# Options for the GNU C compiler only
|
||||
if ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "Intel" )
|
||||
|
@ -182,17 +186,23 @@ if ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_
|
|||
# define some warning flags
|
||||
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wno-unused-parameter -Wdeclaration-after-statement" )
|
||||
|
||||
# prepend to build type specific flags, to allow users to override
|
||||
set ( CMAKE_C_FLAGS_DEBUG "-g -DDEBUG ${CMAKE_C_FLAGS_DEBUG}" )
|
||||
|
||||
if ( CMAKE_C_COMPILER_ID STREQUAL "Intel" )
|
||||
# icc needs the restrict flag to recognize C99 restrict pointers
|
||||
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -restrict" )
|
||||
else () # not intel
|
||||
# gcc and clang support bad function cast and alignment warnings; add them as well.
|
||||
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wbad-function-cast -Wcast-align" )
|
||||
|
||||
if ( enable-ubsan )
|
||||
set ( CMAKE_C_FLAGS "-fsanitize=undefined ${CMAKE_C_FLAGS}" )
|
||||
set ( CMAKE_EXE_LINKER_FLAGS "-fsanitize=undefined ${CMAKE_EXE_LINKER_FLAGS}" )
|
||||
set ( CMAKE_SHARED_LINKER_FLAGS "-fsanitize=undefined ${CMAKE_SHARED_LINKER_FLAGS}" )
|
||||
set ( ENABLE_UBSAN 1 )
|
||||
endif ( enable-ubsan )
|
||||
endif (CMAKE_C_COMPILER_ID STREQUAL "Intel" )
|
||||
|
||||
# prepend to build type specific flags, to allow users to override
|
||||
set ( CMAKE_C_FLAGS_DEBUG "-g -DDEBUG -fsanitize=undefined ${CMAKE_C_FLAGS_DEBUG}" )
|
||||
|
||||
endif ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "Intel" )
|
||||
|
||||
# Windows
|
||||
|
@ -598,7 +608,32 @@ else ( enable-midishare )
|
|||
unset ( MidiShare_LIBS CACHE )
|
||||
endif ( enable-midishare )
|
||||
|
||||
unset ( OPENSLES_SUPPORT CACHE )
|
||||
unset ( OpenSLES_LIBS CACHE )
|
||||
if ( enable-opensles )
|
||||
check_include_file ( SLES/OpenSLES.h OPENSLES_SUPPORT )
|
||||
if ( OPENSLES_SUPPORT )
|
||||
find_library ( OpenSLES_LIBS OpenSLES )
|
||||
if ( NOT OpenSLES_LIBS )
|
||||
unset ( OPENSLES_SUPPORT )
|
||||
endif ( NOT OpenSLES_LIBS )
|
||||
endif ( OPENSLES_SUPPORT )
|
||||
endif ( enable-opensles )
|
||||
|
||||
unset ( OBOE_SUPPORT CACHE )
|
||||
unset ( OBOE_LIBS CACHE )
|
||||
if ( enable-oboe )
|
||||
# enable C++ as it's needed for oboe
|
||||
enable_language ( CXX )
|
||||
pkg_check_modules ( OBOE oboe-1.0 )
|
||||
if ( OBOE_FOUND )
|
||||
set ( OBOE_SUPPORT 1 )
|
||||
set ( OBOE_LIBS ${OBOE_LIBRARIES} )
|
||||
endif ( OBOE_FOUND )
|
||||
endif ( enable-oboe )
|
||||
|
||||
unset ( WITH_READLINE CACHE )
|
||||
unset ( READLINE_LIBS CACHE )
|
||||
if ( enable-readline )
|
||||
find_package ( Readline )
|
||||
set ( FOUND_READLINE ${HAVE_READLINE} )
|
||||
|
@ -606,8 +641,6 @@ if ( enable-readline )
|
|||
set ( WITH_READLINE 1 )
|
||||
set ( READLINE_LIBS ${READLINE_LIBRARIES} )
|
||||
endif ( HAVE_READLINE )
|
||||
else ( enable-readline )
|
||||
unset ( READLINE_LIBS CACHE )
|
||||
endif ( enable-readline )
|
||||
|
||||
unset ( ENABLE_MIXER_THREADS CACHE )
|
||||
|
@ -649,6 +682,7 @@ link_directories (
|
|||
${PORTAUDIO_LIBRARY_DIRS}
|
||||
${LIBSNDFILE_LIBRARY_DIRS}
|
||||
${DBUS_LIBRARY_DIRS}
|
||||
${OBOE_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
# Process subdirectories
|
||||
|
|
|
@ -50,6 +50,18 @@ else ( OSS_SUPPORT )
|
|||
message ( "OSS: no" )
|
||||
endif ( OSS_SUPPORT )
|
||||
|
||||
if ( OPENSLES_SUPPORT )
|
||||
message ( "OpenSLES: yes" )
|
||||
else ( OPENSLES_SUPPORT )
|
||||
message ( "OpenSLES: no" )
|
||||
endif ( OPENSLES_SUPPORT )
|
||||
|
||||
if ( OBOE_SUPPORT )
|
||||
message ( "Oboe: yes" )
|
||||
else ( OBOE_SUPPORT )
|
||||
message ( "Oboe: no" )
|
||||
endif ( OBOE_SUPPORT )
|
||||
|
||||
if ( MIDISHARE_SUPPORT )
|
||||
message ( "MidiShare: yes" )
|
||||
else ( MIDISHARE_SUPPORT )
|
||||
|
@ -188,4 +200,10 @@ else ( ENABLE_FPECHECK )
|
|||
message ( "Check FPE (debug): no" )
|
||||
endif ( ENABLE_FPECHECK )
|
||||
|
||||
if ( ENABLE_UBSAN )
|
||||
message ( "UBSan (debug): yes" )
|
||||
else ( ENABLE_UBSAN )
|
||||
message ( "UBSan (debug): no" )
|
||||
endif ( ENABLE_UBSAN )
|
||||
|
||||
message ( "**************************************************************\n\n" )
|
||||
|
|
2
doc/android/.gitignore
vendored
Normal file
2
doc/android/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
external
|
||||
|
130
doc/android/Makefile.android
Normal file
130
doc/android/Makefile.android
Normal file
|
@ -0,0 +1,130 @@
|
|||
#
|
||||
# The public targets in this Makefile are: build, clean, wipe
|
||||
#
|
||||
# What `build` target does:
|
||||
#
|
||||
# - build cerbero to build glib
|
||||
# - build glib-2.0.so and many other dependency shared libraries
|
||||
# - build Oboe shared library
|
||||
# - build libfluidsynth.so
|
||||
#
|
||||
# Android app developers are supposed to copy all those shared
|
||||
# libraries into their apks (per ABI).
|
||||
#
|
||||
PWD=$(shell pwd)
|
||||
CERBERO=$(PWD)/external/cerbero
|
||||
OBOE=$(PWD)/external/oboe
|
||||
CMAKE=cmake
|
||||
|
||||
ANDROID_NDK = $(PWD)/external/cerbero/build/android-ndk-18
|
||||
|
||||
ABIS_SIMPLE = x86 x86-64 armv7 arm64
|
||||
|
||||
|
||||
DIST_PATH=$(CERBERO)/build/dist
|
||||
OBOE_BUILD_PATH=$(OBOE)/build
|
||||
|
||||
all: build
|
||||
|
||||
.PHONY: prepare
|
||||
prepare: checkout-oboe checkout-cerbero
|
||||
for abi in $(ABIS_SIMPLE) ; do \
|
||||
cd $(CERBERO) && ./cerbero-uninstalled -c config/cross-android-$$abi.cbc bootstrap && cd $(PWD) ; \
|
||||
done
|
||||
|
||||
.PHONY: checkout-oboe
|
||||
checkout-oboe: $(OBOE)
|
||||
cd $(OBOE) && git checkout 9bf3943
|
||||
|
||||
$(OBOE):
|
||||
git clone https://github.com/Google/oboe.git $(OBOE)
|
||||
|
||||
.PHONY: checkout-cerbero
|
||||
checkout-cerbero: $(CERBERO)
|
||||
cd $(CERBERO) && git checkout 7a6fd79
|
||||
|
||||
$(CERBERO):
|
||||
git clone https://github.com/atsushieno/cerbero.git $(CERBERO)
|
||||
|
||||
.PHONY: build
|
||||
build: build-oboe dist-oboe build-deps-cerbero dist-deps-cerbero build-fluidsynth dist-fluidsynth build-fluidsynth-assetloader dist-fluidsynth-assetloader
|
||||
|
||||
.PHONY: build-deps-cerbero
|
||||
build-deps-cerbero:
|
||||
for abi in $(ABIS_SIMPLE) ; do \
|
||||
cd $(CERBERO) && ./cerbero-uninstalled -c config/cross-android-$$abi.cbc build glib && cd $(PWD) ; \
|
||||
done
|
||||
|
||||
define run_make_abi_target
|
||||
make -f Makefile.android BUILD_ABI=$(1) A_ABI=$(2) $(3)
|
||||
endef
|
||||
define run_make_abi_target-unsafe
|
||||
if make -f Makefile.android BUILD_ABI=$(1) A_ABI=$(2) $(3) ; then \
|
||||
echo "ignore failure for $(1)..." ; \
|
||||
fi
|
||||
endef
|
||||
|
||||
define run_make_for_all_abi
|
||||
$(call run_make_abi_target,x86,x86,$(1) )
|
||||
$(call run_make_abi_target,x86_64,x86_64,$(1) )
|
||||
$(call run_make_abi_target,armv7,armeabi-v7a,$(1) )
|
||||
$(call run_make_abi_target-unsafe,arm64,arm64-v8a,$(1) )
|
||||
endef
|
||||
|
||||
.PHONY: dist-deps-cerbero
|
||||
dist-deps-cerbero:
|
||||
$(call run_make_for_all_abi, dist-deps-cerbero-one)
|
||||
|
||||
.PHONY: dist-fluidsynth
|
||||
dist-fluidsynth:
|
||||
$(call run_make_for_all_abi, dist-fluidsynth-one)
|
||||
|
||||
.PHONY: build-oboe
|
||||
build-oboe:
|
||||
$(call run_make_for_all_abi, build-oboe-one)
|
||||
|
||||
.PHONY: dist-oboe
|
||||
dist-oboe:
|
||||
$(call run_make_for_all_abi, dist-oboe-one)
|
||||
|
||||
.PHONY: build-fluidsynth
|
||||
build-fluidsynth:
|
||||
$(call run_make_for_all_abi, build-fluidsynth-one)
|
||||
|
||||
build-fluidsynth-one:
|
||||
mkdir -p build/$(A_ABI) && cd build/$(A_ABI) && \
|
||||
LD_RUN_PATH=$(DIST_PATH)/android-$(BUILD_ABI)/lib:$(OBOE_BUILD_PATH)/$(A_ABI) LD_LIBRARY_PATH=$(DIST_PATH)/android_$(BUILD_ABI)/lib PKG_CONFIG_PATH=$(DIST_PATH)/android_$(BUILD_ABI)/lib/pkgconfig/:$(OBOE_BUILD_PATH)/$(A_ABI) \
|
||||
$(CMAKE) -DCMAKE_INSTALL_PREFIX=$(PWD)/dist/$(A_ABI) -DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK)/build/cmake/android.toolchain.cmake -Denable-opensles=on -Denable-oboe=on -Denable-jack=off -Denable-oss=off -Denable-pulseaudio=off -Denable-libsndfile=off -Denable-dbus=off -Denable-debug=on -DANDROID_NATIVE_API_LEVEL=android-27 -DANDROID_PLATFORM=android-27 -DANDROID_ABI=$(A_ABI) ../../../.. && make
|
||||
|
||||
build-oboe-one:
|
||||
mkdir -p $(OBOE)/build/$(A_ABI) && cd $(OBOE)/build/$(A_ABI) && \
|
||||
$(CMAKE) -DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK)/build/cmake/android.toolchain.cmake -DANDROID_ABI=$(A_ABI) -DANDROID_NATIVE_API_LEVEL=android-27 -DANDROID_PLATFORM=android-27 -DBUILD_SHARED_LIBS=on ../.. && make
|
||||
cp oboe-1.0.pc $(OBOE)/build/$(A_ABI)
|
||||
|
||||
dist-oboe-one:
|
||||
mkdir -p dist/$(A_ABI) && cp $(OBOE)/build/$(A_ABI)/*.so dist/$(A_ABI)/
|
||||
|
||||
dist-deps-cerbero-one:
|
||||
mkdir -p dist/$(A_ABI) && cd dist/$(A_ABI) && cp ../../external/cerbero/build/dist/android_$(BUILD_ABI)/lib/*.so . && cd ../..
|
||||
|
||||
dist-fluidsynth-one:
|
||||
mkdir -p dist/$(A_ABI) && cd dist/$(A_ABI) && cp ../../build/$(A_ABI)/src/libfluidsynth.so . && cd ../..
|
||||
cp -r ../../include/fluidsynth build/$(A_ABI)/include/
|
||||
|
||||
build-fluidsynth-assetloader:
|
||||
cd fluidsynth-assetloader && ./ext-build.sh
|
||||
|
||||
dist-fluidsynth-assetloader:
|
||||
cp fluidsynth-assetloader/build/x86/*.so dist/x86/
|
||||
cp fluidsynth-assetloader/build/x86_64/*.so dist/x86_64/
|
||||
cp fluidsynth-assetloader/build/armeabi-v7a/*.so dist/armeabi-v7a/
|
||||
cp fluidsynth-assetloader/build/arm64-v8a/*.so dist/arm64-v8a/
|
||||
|
||||
clean:
|
||||
rm -rf dist/* build/* external/oboe/build/* obj/local/* fluidsynth-asset-loader/build/*
|
||||
|
||||
.PHONY: wipe
|
||||
wipe: $(CERBERO)
|
||||
for abi in $(ABIS_SIMPLE) ; do \
|
||||
cd $(CERBERO) && ./cerbero-uninstalled -c config/cross-android-$$abi.cbc wipe && cd ../.. ; \
|
||||
done
|
38
doc/android/README.Android.md
Normal file
38
doc/android/README.Android.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Android support in Fluidsynth
|
||||
|
||||
Fluidsynth supports Android audio outputs by Oboe and OpenSLES audio drivers.
|
||||
|
||||
Android also has Android MIDI API which is exposed only in Android Java API, but it is not exposed as a native API, therefore there is no `mdriver` support for Android. There is an example MidiDeviceService implementation for Fluidsynth at: https://github.com/atsushieno/fluidsynth-midi-service-j
|
||||
|
||||
## Usage
|
||||
|
||||
`libfluidsynth.so` and `libfluidsynth-assetloader.so` are the library that should be packaged into apk. The latter is for asset-based "sfloader".
|
||||
|
||||
By default, "oboe" is the default driver for Android. You can also explicitly specify "opensles" instead, with "audio.driver" setting:
|
||||
|
||||
```
|
||||
fluid_settings_setstr (settings_handle, "audio.driver", "opensles");
|
||||
```
|
||||
|
||||
## Custom SoundFont loader
|
||||
|
||||
Since Android file access is quite limited and there is no common place
|
||||
to store soundfonts unlike Linux desktop (e.g. `/usr/share/sounds/sf2`), you
|
||||
will most likely have to provide custom soundfont loader.
|
||||
|
||||
Since version 2.0.0 Fluidsynth comes with `fluid_sfloader_set_callbacks()` which brings
|
||||
[customizible file/stream reader](https://github.com/FluidSynth/fluidsynth/issues/241) (open/read/seek/tell/close). It is useful to implement simplified
|
||||
custom SF loader e.g. with Android assets or OBB streams.
|
||||
|
||||
The Android implementation is in separate library called `libfluidsynth-assetloader.so`. It comes with native Asset sfloader. However, its usage is a bit tricky because AssetManager needs to be passed from Java code (even though we use AAssetManager API).
|
||||
Use `Java_fluidsynth_androidextensions_NativeHandler_setAssetManagerContext()` to initialize the this loader, then call `new_fluid_android_asset_sfloader()` to create a new sfloader. If you already have AAssetManager instance, then the first JNI function is ignorable and you only have to specify the manager to the second function.
|
||||
|
||||
There is [an example source code](https://github.com/atsushieno/fluidsynth-midi-service-j/blob/a2a56b/fluidsynthjna/src/main/java/fluidsynth/androidextensions/AndroidNativeAssetSoundFontLoader.kt#L17) on how to do it.
|
||||
|
||||
## Building
|
||||
|
||||
By default, you are supposed to provide `PKG_CONFIG_PATH` to glib etc. as well as oboe. There is nothing special.
|
||||
|
||||
However, in reality, Oboe does not come up with an official package specification, so you will have to create it manually... unless you use `oboe-1.0.pc` in this directory as well as the build system set up here.
|
||||
|
||||
There are "non-normative" build scripts i.e. `Makefile.android` and a couple of helper files in this directory. In case you don't have any dependencies such as glib for Android, then it would be helpful.
|
18
doc/android/fluidsynth-assetloader/CMakeLists.txt
Normal file
18
doc/android/fluidsynth-assetloader/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
cmake_minimum_required(VERSION 3.6.0)
|
||||
|
||||
project ( fluidsynth-assetloader C )
|
||||
|
||||
set ( fluidsynth-assetloader_sources fluid_androidasset.c )
|
||||
|
||||
add_library ( fluidsynth-assetloader SHARED ${fluidsynth-assetloader_sources} )
|
||||
|
||||
target_compile_options ( fluidsynth-assetloader
|
||||
PRIVATE -v
|
||||
PRIVATE -Wall
|
||||
PRIVATE "$<$<CONFIG:DEBUG>:-Werror>") # Only include -Werror when building debug config
|
||||
|
||||
include_directories ( ../../../include )
|
||||
|
||||
set ( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L../../../dist/${ANDROID_ABI} -lfluidsynth" )
|
||||
|
||||
target_link_libraries ( fluidsynth-assetloader PRIVATE log android )
|
16
doc/android/fluidsynth-assetloader/ext-build.sh
Executable file
16
doc/android/fluidsynth-assetloader/ext-build.sh
Executable file
|
@ -0,0 +1,16 @@
|
|||
PWD=`pwd`
|
||||
|
||||
ABIS="x86 x86_64 armeabi-v7a arm64-v8a"
|
||||
|
||||
HOST_OS=`uname | tr [:upper:] [:lower:]`
|
||||
|
||||
ANDROID_NDK_PATH=~/android-sdk-$HOST_OS/ndk-bundle
|
||||
CMAKEFILE=$ANDROID_NDK_PATH/build/cmake/android.toolchain.cmake
|
||||
|
||||
for A_ABI in $ABIS ; do
|
||||
mkdir -p build/$A_ABI && \
|
||||
cd build/$A_ABI && \
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=$CMAKEFILE -DANDROID_PLATFORM=android-27 -DANDROID_ABI=$A_ABI ../.. && \
|
||||
make &&
|
||||
cd ../.. ;
|
||||
done
|
110
doc/android/fluidsynth-assetloader/fluid_androidasset.c
Normal file
110
doc/android/fluidsynth-assetloader/fluid_androidasset.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/* FluidSynth - A Software Synthesizer
|
||||
*
|
||||
* Copyright (C) 2003 Peter Hanappe and others.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
#if defined(ANDROID) || defined(__DOXYGEN__)
|
||||
|
||||
#define FLUIDSYNTH_API
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
#include "fluid_androidasset.h"
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/asset_manager_jni.h>
|
||||
|
||||
|
||||
AAssetManager *fluid_android_asset_manager;
|
||||
|
||||
fluid_sfloader_t* new_fluid_android_asset_sfloader(fluid_settings_t *settings, void *assetManager)
|
||||
{
|
||||
fluid_sfloader_t *loader;
|
||||
|
||||
if (settings == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!fluid_android_asset_manager)
|
||||
fluid_android_asset_manager = (AAssetManager*) assetManager;
|
||||
|
||||
if (fluid_android_asset_manager == NULL)
|
||||
return NULL;
|
||||
|
||||
loader = new_fluid_defsfloader(settings);
|
||||
if (loader == NULL)
|
||||
return NULL;
|
||||
|
||||
fluid_sfloader_set_callbacks(loader,
|
||||
asset_open,
|
||||
asset_read,
|
||||
asset_seek,
|
||||
asset_tell,
|
||||
asset_close);
|
||||
|
||||
return loader;
|
||||
}
|
||||
|
||||
/* This is a compromised solution for JNAerator for that 1) it cannot handle jobject with JNIEnv as parameters, and that 2) the returned pointer can be converted in the same manner that JNAerated methods. (Most likely my JNA usage issue but no one has answer for it.) */
|
||||
void Java_fluidsynth_androidextensions_NativeHandler_setAssetManagerContext(JNIEnv *env, jobject _this, jobject assetManager)
|
||||
{
|
||||
if (assetManager == NULL)
|
||||
return;
|
||||
|
||||
fluid_android_asset_manager = AAssetManager_fromJava (env, assetManager);
|
||||
}
|
||||
|
||||
void *asset_open(const char *path)
|
||||
{
|
||||
if (fluid_android_asset_manager == NULL)
|
||||
return NULL;
|
||||
|
||||
return AAssetManager_open (fluid_android_asset_manager, path, AASSET_MODE_RANDOM);
|
||||
}
|
||||
|
||||
int asset_close(void *handle)
|
||||
{
|
||||
AAsset *asset;
|
||||
|
||||
asset = (AAsset*) handle;
|
||||
AAsset_close (asset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long asset_tell(void *handle)
|
||||
{
|
||||
AAsset *asset;
|
||||
|
||||
asset = (AAsset*) handle;
|
||||
return AAsset_getLength(asset) - AAsset_getRemainingLength(asset);
|
||||
}
|
||||
|
||||
int asset_seek(void *handle, long offset, int origin)
|
||||
{
|
||||
AAsset *asset;
|
||||
|
||||
asset = (AAsset*) handle;
|
||||
return AAsset_seek (asset, (off_t) offset, origin);
|
||||
}
|
||||
|
||||
int asset_read(void *buf, int count, void *handle)
|
||||
{
|
||||
AAsset *asset;
|
||||
|
||||
asset = (AAsset*) handle;
|
||||
return AAsset_read (asset, buf, (size_t) count);
|
||||
}
|
||||
|
||||
#endif /* if defined(ANDROID) || defined(__DOXYGEN__) */
|
45
doc/android/fluidsynth-assetloader/fluid_androidasset.h
Normal file
45
doc/android/fluidsynth-assetloader/fluid_androidasset.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* FluidSynth - A Software Synthesizer
|
||||
*
|
||||
* Copyright (C) 2003 Peter Hanappe and others.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _PRIV_FLUID_ANDROIDASSET_H
|
||||
#define _PRIV_FLUID_ANDROIDASSET_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <fluidsynth/types.h>
|
||||
#include <fluidsynth/sfont.h>
|
||||
|
||||
fluid_sfloader_t* new_fluid_android_asset_sfloader(fluid_settings_t *settings, void *assetManager);
|
||||
void Java_fluidsynth_androidextensions_NativeHandler_setAssetManagerContext(JNIEnv *env, jobject _this, jobject assetManager);
|
||||
|
||||
void *asset_open(const char *path);
|
||||
int asset_close(void *handle);
|
||||
long asset_tell(void *handle);
|
||||
int asset_seek(void *handle, long offset, int origin);
|
||||
int asset_read(void *buf, int count, void *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* _PRIV_FLUID_ANDROIDASSET_H */
|
44
doc/android/jni/Android.mk
Normal file
44
doc/android/jni/Android.mk
Normal file
|
@ -0,0 +1,44 @@
|
|||
LOCAL_PATH := $(call my-dir)
|
||||
TARGET_PLATFORM := android-27
|
||||
|
||||
GLIB_LIB = ../dep/$(APP_ABI)/
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := glib-2.0
|
||||
LOCAL_SRC_FILES := $(GLIB_LIB)/libglib-2.0.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := iconv
|
||||
LOCAL_SRC_FILES := $(GLIB_LIB)/libiconv.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := intl
|
||||
LOCAL_SRC_FILES := $(GLIB_LIB)/libintl.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := oboe
|
||||
LOCAL_SRC_FILES := $(GLIB_LIB)/liboboe.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := fluidsynth_static
|
||||
LOCAL_SRC_FILES := ../dep/$(APP_ABI)/libfluidsynth.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
LOCAL_MODULE := fluidsynth
|
||||
|
||||
ifeq ($(NDK_DEBUG),1)
|
||||
cmd-strip :=
|
||||
endif
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := glib-2.0 iconv intl oboe
|
||||
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := fluidsynth_static
|
||||
|
||||
LOCAL_LDLIBS := -lc -lOpenSLES -ldl -llog -landroid -L$(LOCAL_PATH)/../dist/$(APP_ABI) -loboe-c
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
2
doc/android/jni/Application.mk
Normal file
2
doc/android/jni/Application.mk
Normal file
|
@ -0,0 +1,2 @@
|
|||
APP_PLATFORM := android-21
|
||||
APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a
|
10
doc/android/oboe-1.0.pc
Normal file
10
doc/android/oboe-1.0.pc
Normal file
|
@ -0,0 +1,10 @@
|
|||
prefix=${pcfiledir}
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}
|
||||
includedir=${prefix}/../../include/
|
||||
|
||||
Name: Oboe
|
||||
Description: Oboe library
|
||||
Version: 1.0.0
|
||||
Libs: -L${libdir} -loboe -landroid -llog -lstdc++
|
||||
Cflags: -I${includedir}
|
|
@ -508,7 +508,7 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
|
|||
<type>str</type>
|
||||
<def>fluidsynth</def>
|
||||
<desc>
|
||||
ID used when creating Jack client connection.
|
||||
Unique identifier used when creating Jack client connection.
|
||||
</desc>
|
||||
</setting>
|
||||
<setting>
|
||||
|
@ -527,6 +527,34 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
|
|||
Jack server to connect to. Defaults to an empty string, which uses default Jack server.
|
||||
</desc>
|
||||
</setting>
|
||||
<setting>
|
||||
<name>oboe.id</name>
|
||||
<type>int</type>
|
||||
<def>0</def>
|
||||
<min>0</min>
|
||||
<max>2147483647</max>
|
||||
<desc>
|
||||
Request an audio device identified device using an ID as pointed out by Oboe's documentation.
|
||||
</desc>
|
||||
</setting>
|
||||
<setting>
|
||||
<name>oboe.sharing-mode</name>
|
||||
<type>str</type>
|
||||
<def>Shared</def>
|
||||
<vals>Shared, Exclusive</vals>
|
||||
<desc>
|
||||
Sets the sharing mode as pointed out by Oboe's documentation.
|
||||
</desc>
|
||||
</setting>
|
||||
<setting>
|
||||
<name>oboe.performance-mode</name>
|
||||
<type>str</type>
|
||||
<def>None</def>
|
||||
<vals>None, PowerSaving, LowLatency</vals>
|
||||
<desc>
|
||||
Sets the performance mode as pointed out by Oboe's documentation.
|
||||
</desc>
|
||||
</setting>
|
||||
<setting>
|
||||
<name>oss.device</name>
|
||||
<type>str</type>
|
||||
|
|
|
@ -220,6 +220,8 @@ Creating the audio driver is straightforward: set the <code>audio.driver</code>
|
|||
- portaudio: PortAudio Library (Mac OS 9 & X, Windows, Linux)
|
||||
- sndman: Apple SoundManager (Mac OS Classic)
|
||||
- dart: DART sound driver (OS/2)
|
||||
- opensles: OpenSL ES (Android)
|
||||
- oboe: Oboe (Android)
|
||||
- file: Driver to output audio to a file
|
||||
- sdl2*: Simple DirectMedia Layer (Linux, Windows, Mac OS X, iOS, Android, FreeBSD, Haiku, etc.)
|
||||
|
||||
|
|
|
@ -126,6 +126,16 @@ if ( AUFILE_SUPPORT )
|
|||
set ( fluid_aufile_SOURCES drivers/fluid_aufile.c )
|
||||
endif ( AUFILE_SUPPORT )
|
||||
|
||||
if ( OPENSLES_SUPPORT )
|
||||
set ( fluid_opensles_SOURCES drivers/fluid_opensles.c )
|
||||
include_directories ( ${OpenSLES_INCLUDE_DIRS} )
|
||||
endif ( OPENSLES_SUPPORT )
|
||||
|
||||
if ( OBOE_SUPPORT )
|
||||
set ( fluid_oboe_SOURCES drivers/fluid_oboe.cpp )
|
||||
include_directories ( ${OBOE_INCLUDE_DIRS} )
|
||||
endif ( OBOE_SUPPORT )
|
||||
|
||||
set ( config_SOURCES ${CMAKE_BINARY_DIR}/config.h )
|
||||
|
||||
set ( libfluidsynth_SOURCES
|
||||
|
@ -257,6 +267,8 @@ add_library ( libfluidsynth-OBJ OBJECT
|
|||
${fluid_jack_SOURCES}
|
||||
${fluid_lash_SOURCES}
|
||||
${fluid_midishare_SOURCES}
|
||||
${fluid_opensles_SOURCES}
|
||||
${fluid_oboe_SOURCES}
|
||||
${fluid_oss_SOURCES}
|
||||
${fluid_portaudio_SOURCES}
|
||||
${fluid_pulse_SOURCES}
|
||||
|
@ -339,6 +351,8 @@ target_link_libraries ( libfluidsynth
|
|||
${COREMIDI_LIBS}
|
||||
${WINDOWS_LIBS}
|
||||
${MidiShare_LIBS}
|
||||
${OpenSLES_LIBS}
|
||||
${OBOE_LIBS}
|
||||
${LIBFLUID_LIBS}
|
||||
)
|
||||
|
||||
|
|
|
@ -172,6 +172,12 @@
|
|||
/* Define to enable OSS driver */
|
||||
#cmakedefine OSS_SUPPORT @OSS_SUPPORT@
|
||||
|
||||
/* Define to enable OPENSLES driver */
|
||||
#cmakedefine OPENSLES_SUPPORT @OPENSLES_SUPPORT@
|
||||
|
||||
/* Define to enable Oboe driver */
|
||||
#cmakedefine OBOE_SUPPORT @OBOE_SUPPORT@
|
||||
|
||||
/* Name of package */
|
||||
#cmakedefine PACKAGE "@PACKAGE@"
|
||||
|
||||
|
|
|
@ -80,6 +80,26 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
|
|||
},
|
||||
#endif
|
||||
|
||||
#if OBOE_SUPPORT
|
||||
{
|
||||
"oboe",
|
||||
new_fluid_oboe_audio_driver,
|
||||
NULL,
|
||||
delete_fluid_oboe_audio_driver,
|
||||
fluid_oboe_audio_driver_settings
|
||||
},
|
||||
#endif
|
||||
|
||||
#if OPENSLES_SUPPORT
|
||||
{
|
||||
"opensles",
|
||||
new_fluid_opensles_audio_driver,
|
||||
NULL,
|
||||
delete_fluid_opensles_audio_driver,
|
||||
fluid_opensles_audio_driver_settings
|
||||
},
|
||||
#endif
|
||||
|
||||
#if COREAUDIO_SUPPORT
|
||||
{
|
||||
"coreaudio",
|
||||
|
|
|
@ -66,6 +66,22 @@ void delete_fluid_oss_audio_driver(fluid_audio_driver_t *p);
|
|||
void fluid_oss_audio_driver_settings(fluid_settings_t *settings);
|
||||
#endif
|
||||
|
||||
#if OPENSLES_SUPPORT
|
||||
fluid_audio_driver_t*
|
||||
new_fluid_opensles_audio_driver(fluid_settings_t* settings,
|
||||
fluid_synth_t* synth);
|
||||
void delete_fluid_opensles_audio_driver(fluid_audio_driver_t* p);
|
||||
void fluid_opensles_audio_driver_settings(fluid_settings_t* settings);
|
||||
#endif
|
||||
|
||||
#if OBOE_SUPPORT
|
||||
fluid_audio_driver_t*
|
||||
new_fluid_oboe_audio_driver(fluid_settings_t* settings,
|
||||
fluid_synth_t* synth);
|
||||
void delete_fluid_oboe_audio_driver(fluid_audio_driver_t* p);
|
||||
void fluid_oboe_audio_driver_settings(fluid_settings_t* settings);
|
||||
#endif
|
||||
|
||||
#if COREAUDIO_SUPPORT
|
||||
fluid_audio_driver_t *new_fluid_core_audio_driver(fluid_settings_t *settings,
|
||||
fluid_synth_t *synth);
|
||||
|
|
225
src/drivers/fluid_oboe.cpp
Normal file
225
src/drivers/fluid_oboe.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
/* FluidSynth - A Software Synthesizer
|
||||
*
|
||||
* Copyright (C) 2003 Peter Hanappe and others.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* fluid_oboe.c
|
||||
*
|
||||
* Audio driver for Android Oboe.
|
||||
*
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "fluid_adriver.h"
|
||||
#include "fluid_settings.h"
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#if OBOE_SUPPORT
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
static const int NUM_CHANNELS = 2;
|
||||
|
||||
class OboeAudioStreamCallback;
|
||||
|
||||
/** fluid_oboe_audio_driver_t
|
||||
*
|
||||
* This structure should not be accessed directly. Use audio port
|
||||
* functions instead.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
fluid_audio_driver_t driver;
|
||||
fluid_synth_t *synth;
|
||||
int cont;
|
||||
OboeAudioStreamCallback *oboe_callback;
|
||||
AudioStream *stream;
|
||||
} fluid_oboe_audio_driver_t;
|
||||
|
||||
|
||||
class OboeAudioStreamCallback : public AudioStreamCallback
|
||||
{
|
||||
public:
|
||||
|
||||
OboeAudioStreamCallback(void *userData)
|
||||
: user_data(userData)
|
||||
{
|
||||
}
|
||||
|
||||
DataCallbackResult onAudioReady(AudioStream *stream, void *audioData, int32_t numFrames)
|
||||
{
|
||||
fluid_oboe_audio_driver_t *dev = static_cast<fluid_oboe_audio_driver_t *>(this->user_data);
|
||||
|
||||
if(!dev->cont)
|
||||
{
|
||||
return DataCallbackResult::Stop;
|
||||
}
|
||||
|
||||
if(stream->getFormat() == AudioFormat::Float)
|
||||
{
|
||||
fluid_synth_write_float(dev->synth, numFrames, static_cast<float *>(audioData), 0, 2, static_cast<float *>(audioData), 1, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
fluid_synth_write_s16(dev->synth, numFrames, static_cast<short *>(audioData), 0, 2, static_cast<short *>(audioData), 1, 2);
|
||||
}
|
||||
|
||||
return DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
private:
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
void fluid_oboe_audio_driver_settings(fluid_settings_t *settings)
|
||||
{
|
||||
fluid_settings_register_int(settings, "audio.oboe.id", 0, 0, 0x7FFFFFFF, 0);
|
||||
|
||||
fluid_settings_register_str(settings, "audio.oboe.sharing-mode", "Shared", 0);
|
||||
fluid_settings_add_option(settings, "audio.oboe.sharing-mode", "Shared");
|
||||
fluid_settings_add_option(settings, "audio.oboe.sharing-mode", "Exclusive");
|
||||
|
||||
fluid_settings_register_str(settings, "audio.oboe.performance-mode", "None", 0);
|
||||
fluid_settings_add_option(settings, "audio.oboe.performance-mode", "None");
|
||||
fluid_settings_add_option(settings, "audio.oboe.performance-mode", "PowerSaving");
|
||||
fluid_settings_add_option(settings, "audio.oboe.performance-mode", "LowLatency");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* new_fluid_oboe_audio_driver
|
||||
*/
|
||||
fluid_audio_driver_t *
|
||||
new_fluid_oboe_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
||||
{
|
||||
Result result;
|
||||
fluid_oboe_audio_driver_t *dev;
|
||||
AudioStreamBuilder builder_obj;
|
||||
AudioStreamBuilder *builder = &builder_obj;
|
||||
AudioStream *stream;
|
||||
|
||||
int period_frames;
|
||||
double sample_rate;
|
||||
int is_sample_format_float;
|
||||
int device_id;
|
||||
int sharing_mode; // 0: Shared, 1: Exclusive
|
||||
int performance_mode; // 0: None, 1: PowerSaving, 2: LowLatency
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
dev = FLUID_NEW(fluid_oboe_audio_driver_t);
|
||||
|
||||
if(dev == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FLUID_MEMSET(dev, 0, sizeof(fluid_oboe_audio_driver_t));
|
||||
|
||||
dev->synth = synth;
|
||||
dev->oboe_callback = new(std::nothrow) OboeAudioStreamCallback(dev);
|
||||
|
||||
if(!dev->oboe_callback)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
fluid_settings_getint(settings, "audio.period-size", &period_frames);
|
||||
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
|
||||
is_sample_format_float = fluid_settings_str_equal(settings, "audio.sample-format", "float");
|
||||
fluid_settings_getint(settings, "audio.oboe.id", &device_id);
|
||||
sharing_mode =
|
||||
fluid_settings_str_equal(settings, "audio.oboe.sharing-mode", "Exclusive") ? 1 : 0;
|
||||
performance_mode =
|
||||
fluid_settings_str_equal(settings, "audio.oboe.performance-mode", "PowerSaving") ? 1 :
|
||||
fluid_settings_str_equal(settings, "audio.oboe.performance-mode", "LowLatency") ? 2 : 0;
|
||||
|
||||
builder->setDeviceId(device_id)
|
||||
->setDirection(Direction::Output)
|
||||
->setChannelCount(NUM_CHANNELS)
|
||||
->setSampleRate(sample_rate)
|
||||
->setFramesPerCallback(period_frames)
|
||||
->setFormat(is_sample_format_float ? AudioFormat::Float : AudioFormat::I16)
|
||||
->setSharingMode(sharing_mode == 1 ? SharingMode::Exclusive : SharingMode::Shared)
|
||||
->setPerformanceMode(
|
||||
performance_mode == 1 ? PerformanceMode::PowerSaving :
|
||||
performance_mode == 2 ? PerformanceMode::LowLatency : PerformanceMode::None)
|
||||
->setUsage(Usage::Media)
|
||||
->setContentType(ContentType::Music)
|
||||
->setCallback(dev->oboe_callback);
|
||||
|
||||
result = builder->openStream(&stream);
|
||||
dev->stream = stream;
|
||||
|
||||
if(result != Result::OK)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
dev->cont = 1;
|
||||
|
||||
FLUID_LOG(FLUID_INFO, "Using Oboe driver");
|
||||
|
||||
stream->start();
|
||||
|
||||
return reinterpret_cast<fluid_audio_driver_t *>(dev);
|
||||
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Unexpected Oboe driver initialization error");
|
||||
}
|
||||
|
||||
error_recovery:
|
||||
delete_fluid_oboe_audio_driver(reinterpret_cast<fluid_audio_driver_t *>(dev));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void delete_fluid_oboe_audio_driver(fluid_audio_driver_t *p)
|
||||
{
|
||||
fluid_oboe_audio_driver_t *dev = reinterpret_cast<fluid_oboe_audio_driver_t *>(p);
|
||||
|
||||
fluid_return_if_fail(dev != NULL);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
dev->cont = 0;
|
||||
|
||||
if(dev->stream != NULL)
|
||||
{
|
||||
dev->stream->stop();
|
||||
dev->stream->close();
|
||||
}
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
delete dev->oboe_callback;
|
||||
|
||||
FLUID_FREE(dev);
|
||||
}
|
||||
|
||||
#endif // OBOE_SUPPORT
|
||||
|
333
src/drivers/fluid_opensles.c
Normal file
333
src/drivers/fluid_opensles.c
Normal file
|
@ -0,0 +1,333 @@
|
|||
/* FluidSynth - A Software Synthesizer
|
||||
*
|
||||
* Copyright (C) 2003 Peter Hanappe and others.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* fluid_opensles.c
|
||||
*
|
||||
* Audio driver for OpenSLES.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "fluid_adriver.h"
|
||||
|
||||
#if OPENSLES_SUPPORT
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
static const int NUM_CHANNELS = 2;
|
||||
|
||||
/** fluid_opensles_audio_driver_t
|
||||
*
|
||||
* This structure should not be accessed directly. Use audio port
|
||||
* functions instead.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
fluid_audio_driver_t driver;
|
||||
SLObjectItf engine;
|
||||
SLObjectItf output_mix_object;
|
||||
SLObjectItf audio_player;
|
||||
SLPlayItf audio_player_interface;
|
||||
SLAndroidSimpleBufferQueueItf player_buffer_queue_interface;
|
||||
|
||||
void *synth;
|
||||
int period_frames;
|
||||
|
||||
int is_sample_format_float;
|
||||
|
||||
/* used only by callback mode */
|
||||
short *sles_buffer_short;
|
||||
float *sles_buffer_float;
|
||||
|
||||
int cont;
|
||||
|
||||
double sample_rate;
|
||||
} fluid_opensles_audio_driver_t;
|
||||
|
||||
|
||||
static void opensles_callback(SLAndroidSimpleBufferQueueItf caller, void *pContext);
|
||||
static void process_fluid_buffer(fluid_opensles_audio_driver_t *dev);
|
||||
|
||||
void fluid_opensles_audio_driver_settings(fluid_settings_t *settings)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* new_fluid_opensles_audio_driver
|
||||
*/
|
||||
fluid_audio_driver_t *
|
||||
new_fluid_opensles_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
||||
{
|
||||
SLresult result;
|
||||
fluid_opensles_audio_driver_t *dev;
|
||||
double sample_rate;
|
||||
int period_size;
|
||||
int realtime_prio = 0;
|
||||
int is_sample_format_float;
|
||||
SLEngineItf engine_interface;
|
||||
|
||||
dev = FLUID_NEW(fluid_opensles_audio_driver_t);
|
||||
|
||||
if(dev == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FLUID_MEMSET(dev, 0, sizeof(*dev));
|
||||
|
||||
fluid_settings_getint(settings, "audio.period-size", &period_size);
|
||||
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
|
||||
fluid_settings_getint(settings, "audio.realtime-prio", &realtime_prio);
|
||||
is_sample_format_float = fluid_settings_str_equal(settings, "audio.sample-format", "float");
|
||||
|
||||
dev->synth = synth;
|
||||
dev->is_sample_format_float = is_sample_format_float;
|
||||
dev->period_frames = period_size;
|
||||
dev->sample_rate = sample_rate;
|
||||
dev->cont = 1;
|
||||
|
||||
result = slCreateEngine(&(dev->engine), 0, NULL, 0, NULL, NULL);
|
||||
|
||||
if(!dev->engine)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Failed to create OpenSLES connection");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
result = (*dev->engine)->Realize(dev->engine, SL_BOOLEAN_FALSE);
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
result = (*dev->engine)->GetInterface(dev->engine, SL_IID_ENGINE, &engine_interface);
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
result = (*engine_interface)->CreateOutputMix(engine_interface, &dev->output_mix_object, 0, 0, 0);
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
result = (*dev->output_mix_object)->Realize(dev->output_mix_object, SL_BOOLEAN_FALSE);
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_buffer_queue =
|
||||
{
|
||||
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
2 /* number of buffers */
|
||||
};
|
||||
SLAndroidDataFormat_PCM_EX format_pcm =
|
||||
{
|
||||
SL_ANDROID_DATAFORMAT_PCM_EX,
|
||||
NUM_CHANNELS,
|
||||
((SLuint32) sample_rate) * 1000,
|
||||
is_sample_format_float ? SL_PCMSAMPLEFORMAT_FIXED_32 : SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
is_sample_format_float ? SL_PCMSAMPLEFORMAT_FIXED_32 : SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
||||
SL_BYTEORDER_LITTLEENDIAN,
|
||||
is_sample_format_float ? SL_ANDROID_PCM_REPRESENTATION_FLOAT : SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT
|
||||
};
|
||||
SLDataSource audio_src =
|
||||
{
|
||||
&loc_buffer_queue,
|
||||
&format_pcm
|
||||
};
|
||||
|
||||
SLDataLocator_OutputMix loc_outmix =
|
||||
{
|
||||
SL_DATALOCATOR_OUTPUTMIX,
|
||||
dev->output_mix_object
|
||||
};
|
||||
SLDataSink audio_sink = {&loc_outmix, NULL};
|
||||
|
||||
const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
|
||||
const SLboolean req1[] = {SL_BOOLEAN_TRUE};
|
||||
result = (*engine_interface)->CreateAudioPlayer(engine_interface,
|
||||
&(dev->audio_player), &audio_src, &audio_sink, 1, ids1, req1);
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
result = (*dev->audio_player)->Realize(dev->audio_player, SL_BOOLEAN_FALSE);
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
result = (*dev->audio_player)->GetInterface(dev->audio_player,
|
||||
SL_IID_PLAY, &(dev->audio_player_interface));
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
result = (*dev->audio_player)->GetInterface(dev->audio_player,
|
||||
SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(dev->player_buffer_queue_interface));
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
if(dev->is_sample_format_float)
|
||||
{
|
||||
dev->sles_buffer_float = FLUID_ARRAY(float, dev->period_frames * NUM_CHANNELS);
|
||||
}
|
||||
else
|
||||
{
|
||||
dev->sles_buffer_short = FLUID_ARRAY(short, dev->period_frames * NUM_CHANNELS);
|
||||
}
|
||||
|
||||
if(dev->sles_buffer_float == NULL && dev->sles_buffer_short == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory.");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
result = (*dev->player_buffer_queue_interface)->RegisterCallback(dev->player_buffer_queue_interface, opensles_callback, dev);
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
if(dev->is_sample_format_float)
|
||||
{
|
||||
(*dev->player_buffer_queue_interface)->Enqueue(dev->player_buffer_queue_interface, dev->sles_buffer_float, dev->period_frames * NUM_CHANNELS * sizeof(float));
|
||||
}
|
||||
else
|
||||
{
|
||||
(*dev->player_buffer_queue_interface)->Enqueue(dev->player_buffer_queue_interface, dev->sles_buffer_short, dev->period_frames * NUM_CHANNELS * sizeof(short));
|
||||
}
|
||||
|
||||
(*dev->audio_player_interface)->SetCallbackEventsMask(dev->audio_player_interface, SL_PLAYEVENT_HEADATEND);
|
||||
result = (*dev->audio_player_interface)->SetPlayState(dev->audio_player_interface, SL_PLAYSTATE_PLAYING);
|
||||
|
||||
if(result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
FLUID_LOG(FLUID_INFO, "Using OpenSLES driver.");
|
||||
|
||||
return (fluid_audio_driver_t *) dev;
|
||||
|
||||
error_recovery:
|
||||
|
||||
delete_fluid_opensles_audio_driver((fluid_audio_driver_t *) dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void delete_fluid_opensles_audio_driver(fluid_audio_driver_t *p)
|
||||
{
|
||||
fluid_opensles_audio_driver_t *dev = (fluid_opensles_audio_driver_t *) p;
|
||||
|
||||
fluid_return_if_fail(dev != NULL);
|
||||
|
||||
dev->cont = 0;
|
||||
|
||||
if(dev->audio_player)
|
||||
{
|
||||
(*dev->audio_player)->Destroy(dev->audio_player);
|
||||
}
|
||||
|
||||
if(dev->output_mix_object)
|
||||
{
|
||||
(*dev->output_mix_object)->Destroy(dev->output_mix_object);
|
||||
}
|
||||
|
||||
if(dev->engine)
|
||||
{
|
||||
(*dev->engine)->Destroy(dev->engine);
|
||||
}
|
||||
|
||||
if(dev->sles_buffer_float)
|
||||
{
|
||||
FLUID_FREE(dev->sles_buffer_float);
|
||||
}
|
||||
|
||||
if(dev->sles_buffer_short)
|
||||
{
|
||||
FLUID_FREE(dev->sles_buffer_short);
|
||||
}
|
||||
|
||||
FLUID_FREE(dev);
|
||||
}
|
||||
|
||||
void opensles_callback(SLAndroidSimpleBufferQueueItf caller, void *pContext)
|
||||
{
|
||||
fluid_opensles_audio_driver_t *dev = (fluid_opensles_audio_driver_t *) pContext;
|
||||
SLresult result;
|
||||
|
||||
process_fluid_buffer(dev);
|
||||
|
||||
if(dev->is_sample_format_float)
|
||||
{
|
||||
result = (*caller)->Enqueue(
|
||||
dev->player_buffer_queue_interface, dev->sles_buffer_float, dev->period_frames * sizeof(float) * NUM_CHANNELS);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (*caller)->Enqueue(
|
||||
dev->player_buffer_queue_interface, dev->sles_buffer_short, dev->period_frames * sizeof(short) * NUM_CHANNELS);
|
||||
}
|
||||
|
||||
/*
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
// Do not simply break at just one single insufficient buffer. Go on.
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void process_fluid_buffer(fluid_opensles_audio_driver_t *dev)
|
||||
{
|
||||
short *out_short = dev->sles_buffer_short;
|
||||
float *out_float = dev->sles_buffer_float;
|
||||
int period_frames = dev->period_frames;
|
||||
|
||||
if(dev->is_sample_format_float)
|
||||
{
|
||||
fluid_synth_write_float(dev->synth, period_frames, out_float, 0, 2, out_float, 1, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
fluid_synth_write_s16(dev->synth, period_frames, out_short, 0, 2, out_short, 1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue