android native improvements
* rename jme_stbi -> jme_decode, which is a new native library that will handle image and audio decoding in native code * add a special version of tremor designed to run on android * adjust the build process to handle these changes
This commit is contained in:
parent
603432790b
commit
5f0c2035c1
BIN
jme3-android-native/TremorAndroid.zip
Normal file
BIN
jme3-android-native/TremorAndroid.zip
Normal file
Binary file not shown.
@ -44,5 +44,6 @@ ext {
|
||||
|
||||
// add each native lib build file
|
||||
apply from: file('openalsoft.gradle')
|
||||
apply from: file('stb_image.gradle')
|
||||
|
||||
// apply from: file('stb_image.gradle')
|
||||
// apply from: file('tremor.gradle')
|
||||
apply from: file('decode.gradle')
|
110
jme3-android-native/decode.gradle
Normal file
110
jme3-android-native/decode.gradle
Normal file
@ -0,0 +1,110 @@
|
||||
String tremorZipFile = "TremorAndroid.zip"
|
||||
String stbiUrl = 'https://raw.githubusercontent.com/nothings/stb/master/stb_image.h'
|
||||
|
||||
// Working directories for the ndk build.
|
||||
String decodeBuildDir = "${buildDir}" + File.separator + 'decode'
|
||||
String decodeBuildJniDir = decodeBuildDir + File.separator + 'jni'
|
||||
String decodeBuildLibsDir = decodeBuildDir + File.separator + 'libs'
|
||||
|
||||
// Pre-compiled libs directory
|
||||
String decodePreCompiledLibsDir = 'libs' + File.separator + 'decode'
|
||||
|
||||
// jME Android Native source files path
|
||||
String decodeSourceDir = 'src/native/jme_decode'
|
||||
|
||||
task downloadStbImage(type: MyDownload) {
|
||||
sourceUrl = stbiUrl
|
||||
target = file('stb_image.h')
|
||||
}
|
||||
|
||||
// Copy stb_image.h to the source directory.
|
||||
task copyStbiFiles(type: Copy) {
|
||||
def sourceDir = file('stb_image.h')
|
||||
def outputDir = file(decodeSourceDir + File.separator + "STBI")
|
||||
from sourceDir
|
||||
into outputDir
|
||||
}
|
||||
copyStbiFiles.dependsOn {
|
||||
def stbiFile = file('stb_image.h')
|
||||
if (!stbiFile.exists()) {
|
||||
downloadStbImage
|
||||
}
|
||||
}
|
||||
|
||||
// Copy libtremor source to the source directory.
|
||||
task copyTremorFiles(type: Copy) {
|
||||
def zipFile = file(tremorZipFile)
|
||||
def outputDir = file(decodeSourceDir + File.separator + "Tremor")
|
||||
|
||||
from (zipTree(zipFile)) {
|
||||
include '*.c'
|
||||
include '*.h'
|
||||
}
|
||||
|
||||
into outputDir
|
||||
}
|
||||
|
||||
// Generate headers via javah
|
||||
task generateJavahHeaders(type: Exec) {
|
||||
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
|
||||
args '-d', decodeSourceDir
|
||||
args '-classpath', project.projectClassPath
|
||||
args "com.jme3.audio.plugins.NativeVorbisFile"
|
||||
args "com.jme3.texture.plugins.AndroidNativeImageLoader"
|
||||
}
|
||||
|
||||
// Copy jME Android native files to jni directory
|
||||
task copySourceToBuild(type: Copy, dependsOn:[copyTremorFiles, copyStbiFiles, generateJavahHeaders]) {
|
||||
def sourceDir = file(decodeSourceDir)
|
||||
def outputDir = file(decodeBuildJniDir)
|
||||
|
||||
from sourceDir
|
||||
into outputDir
|
||||
}
|
||||
|
||||
task buildNativeLib(type: Exec, dependsOn: copySourceToBuild) {
|
||||
workingDir decodeBuildDir
|
||||
executable rootProject.ndkCommandPath
|
||||
args '-j8'
|
||||
}
|
||||
|
||||
task updatePreCompiledLibs(type: Copy, dependsOn: buildNativeLib) {
|
||||
def sourceDir = new File(decodeBuildLibsDir)
|
||||
def outputDir = new File(decodePreCompiledLibsDir)
|
||||
|
||||
from sourceDir
|
||||
into outputDir
|
||||
}
|
||||
|
||||
// Copy pre-compiled libs to build directory (when not building new libs)
|
||||
task copyPreCompiledLibs(type: Copy) {
|
||||
def sourceDir = file(decodePreCompiledLibsDir)
|
||||
def outputDir = file(decodeBuildLibsDir)
|
||||
|
||||
from sourceDir
|
||||
into outputDir
|
||||
}
|
||||
|
||||
if (rootProject.ndkExists) {
|
||||
// build native libs and update stored pre-compiled libs to commit
|
||||
compileJava.dependsOn { updatePreCompiledLibs }
|
||||
} else {
|
||||
// use pre-compiled native libs (not building new ones)
|
||||
compileJava.dependsOn { copyPreCompiledLibs }
|
||||
}
|
||||
|
||||
jar.into("lib") { from decodeBuildLibsDir }
|
||||
|
||||
// Helper class to wrap ant dowload task
|
||||
class MyDownload extends DefaultTask {
|
||||
@Input
|
||||
String sourceUrl
|
||||
|
||||
@OutputFile
|
||||
File target
|
||||
|
||||
@TaskAction
|
||||
void download() {
|
||||
ant.get(src: sourceUrl, dest: target)
|
||||
}
|
||||
}
|
39
jme3-android-native/src/native/jme_decode/Android.mk
Normal file
39
jme3-android-native/src/native/jme_decode/Android.mk
Normal file
@ -0,0 +1,39 @@
|
||||
TARGET_PLATFORM := android-9
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := decodejme
|
||||
|
||||
LOCAL_C_INCLUDES:= \
|
||||
$(LOCAL_PATH) \
|
||||
$(LOCAL_PATH)/Tremor
|
||||
|
||||
LOCAL_CFLAGS := -std=gnu99
|
||||
LOCAL_LDLIBS := -lz -llog -Wl,-s
|
||||
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
LOCAL_CFLAGS+= -D_ARM_ASSEM_
|
||||
endif
|
||||
|
||||
LOCAL_ARM_MODE := arm
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
Tremor/bitwise.c \
|
||||
Tremor/codebook.c \
|
||||
Tremor/dsp.c \
|
||||
Tremor/floor0.c \
|
||||
Tremor/floor1.c \
|
||||
Tremor/floor_lookup.c \
|
||||
Tremor/framing.c \
|
||||
Tremor/info.c \
|
||||
Tremor/mapping0.c \
|
||||
Tremor/mdct.c \
|
||||
Tremor/misc.c \
|
||||
Tremor/res012.c \
|
||||
Tremor/vorbisfile.c \
|
||||
com_jme3_audio_plugins_NativeVorbisFile.c \
|
||||
com_jme3_texture_plugins_AndroidNativeImageLoader.c
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
3
jme3-android-native/src/native/jme_decode/Application.mk
Normal file
3
jme3-android-native/src/native/jme_decode/Application.mk
Normal file
@ -0,0 +1,3 @@
|
||||
APP_PLATFORM := android-9
|
||||
APP_OPTIM := debug
|
||||
APP_ABI := all
|
@ -0,0 +1,177 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "Tremor/ivorbisfile.h"
|
||||
|
||||
#include "com_jme3_audio_plugins_NativeVorbisFile.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <android/log.h>
|
||||
#include <stdio.h>
|
||||
#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, \
|
||||
"NativeVorbisFile", fmt, ##__VA_ARGS__);
|
||||
#else
|
||||
#error We are building in release mode, arent we?
|
||||
#define LOGI(fmt, ...)
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
JNIEnv* env;
|
||||
int fd;
|
||||
}
|
||||
FileDescWrapper;
|
||||
|
||||
static size_t FileDesc_read(void *ptr, size_t size, size_t nmemb, void *datasource)
|
||||
{
|
||||
FileDescWrapper* wrapper = (FileDescWrapper*)datasource;
|
||||
size_t totalRead = read(wrapper->fd, ptr, size * nmemb);
|
||||
|
||||
LOGI("read(%zu) = %zu", size * nmemb, totalRead);
|
||||
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
// off64_t lseek64(int fd, off64_t offset, int whence);
|
||||
static int FileDesc_seek(void *datasource, ogg_int64_t offset, int whence)
|
||||
{
|
||||
FileDescWrapper* wrapper = (FileDescWrapper*)datasource;
|
||||
int result = lseek64(wrapper->fd, offset, whence);
|
||||
|
||||
char* whenceStr;
|
||||
switch (whence) {
|
||||
case SEEK_CUR: whenceStr = "SEEK_CUR"; break;
|
||||
case SEEK_END: whenceStr = "SEEK_END"; break;
|
||||
case SEEK_SET: whenceStr = "SEEK_SET"; break;
|
||||
default: whenceStr = "unknown"; break;
|
||||
}
|
||||
LOGI("seek(%lld, %s) = %d", offset, whenceStr, result);
|
||||
}
|
||||
|
||||
static int FileDesc_close(void *datasource)
|
||||
{
|
||||
FileDescWrapper* wrapper = (FileDescWrapper*)datasource;
|
||||
|
||||
LOGI("close");
|
||||
|
||||
return close(wrapper->fd);
|
||||
}
|
||||
|
||||
static long FileDesc_tell(void *datasource)
|
||||
{
|
||||
FileDescWrapper* wrapper = (FileDescWrapper*)datasource;
|
||||
long result = lseek64(wrapper->fd, 0, SEEK_CUR);
|
||||
|
||||
LOGI("tell = %ld", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ov_callbacks FileDescCallbacks = {
|
||||
FileDesc_read,
|
||||
FileDesc_seek,
|
||||
FileDesc_close,
|
||||
FileDesc_tell
|
||||
};
|
||||
|
||||
static void throwIOException(JNIEnv* env, const char* message)
|
||||
{
|
||||
jclass ioExClazz = (*env)->FindClass(env, "java/io/IOException");
|
||||
(*env)->ThrowNew(env, ioExClazz, message);
|
||||
}
|
||||
|
||||
static jfieldID nvf_field_ovf;
|
||||
static jfieldID nvf_field_seekable;
|
||||
static jfieldID nvf_field_channels;
|
||||
static jfieldID nvf_field_sampleRate;
|
||||
static jfieldID nvf_field_bitRate;
|
||||
static jfieldID nvf_field_totalBytes;
|
||||
static jfieldID nvf_field_duration;
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_nativeInit
|
||||
(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
LOGI("nativeInit");
|
||||
|
||||
nvf_field_ovf = (*env)->GetFieldID(env, clazz, "ovf", "Ljava/nio/ByteBuffer;");;
|
||||
nvf_field_seekable = (*env)->GetFieldID(env, clazz, "seekable", "Z");
|
||||
nvf_field_channels = (*env)->GetFieldID(env, clazz, "channels", "I");
|
||||
nvf_field_sampleRate = (*env)->GetFieldID(env, clazz, "sampleRate", "I");
|
||||
nvf_field_bitRate = (*env)->GetFieldID(env, clazz, "bitRate", "I");
|
||||
nvf_field_totalBytes = (*env)->GetFieldID(env, clazz, "totalBytes", "I");
|
||||
nvf_field_duration = (*env)->GetFieldID(env, clazz, "duration", "F");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_open
|
||||
(JNIEnv *env, jobject nvf, jint fd)
|
||||
{
|
||||
LOGI("open: %d", fd)
|
||||
|
||||
OggVorbis_File* ovf = (OggVorbis_File*) malloc(sizeof(OggVorbis_File));
|
||||
|
||||
FileDescWrapper* wrapper = (FileDescWrapper*) malloc(sizeof(FileDescWrapper));
|
||||
wrapper->fd = fd;
|
||||
wrapper->env = env; // NOTE: every java call has to update this
|
||||
|
||||
int result = ov_open_callbacks((void*)wrapper, ovf, NULL, 0, FileDescCallbacks);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
LOGI("ov_open fail");
|
||||
|
||||
free(ovf);
|
||||
free(wrapper);
|
||||
|
||||
char err[512];
|
||||
sprintf(err, "ov_open failed: %d", result);
|
||||
throwIOException(env, err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LOGI("ov_open OK");
|
||||
jobject ovfBuf = (*env)->NewDirectByteBuffer(env, ovf, sizeof(OggVorbis_File));
|
||||
|
||||
vorbis_info* info = ov_info(ovf, -1);
|
||||
jint total_bytes = ov_pcm_total(ovf, -1);
|
||||
jboolean seekable = ov_seekable(ovf) != 0;
|
||||
jfloat duration = (jfloat) ov_time_total(ovf, -1);
|
||||
|
||||
(*env)->SetObjectField(env, nvf, nvf_field_ovf, ovfBuf);
|
||||
(*env)->SetBooleanField(env, nvf, nvf_field_seekable, seekable);
|
||||
(*env)->SetIntField(env, nvf, nvf_field_channels, info->channels);
|
||||
(*env)->SetIntField(env, nvf, nvf_field_sampleRate, info->rate);
|
||||
(*env)->SetIntField(env, nvf, nvf_field_bitRate, info->bitrate_nominal);
|
||||
(*env)->SetIntField(env, nvf, nvf_field_totalBytes, total_bytes);
|
||||
(*env)->SetFloatField(env, nvf, nvf_field_duration, duration);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_seekTime
|
||||
(JNIEnv *env, jobject nvf, jdouble time)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_read
|
||||
(JNIEnv *env, jobject nvf, jbyteArray buf, jint off, jint len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_readFully
|
||||
(JNIEnv *env, jobject nvf, jobject buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_close
|
||||
(JNIEnv *env, jobject nvf)
|
||||
{
|
||||
LOGI("close");
|
||||
|
||||
jobject ovfBuf = (*env)->GetObjectField(env, nvf, nvf_field_ovf);
|
||||
OggVorbis_File* ovf = (OggVorbis_File*) (*env)->GetDirectBufferAddress(env, ovfBuf);
|
||||
FileDescWrapper* wrapper = (FileDescWrapper*) ovf->datasource;
|
||||
free(wrapper);
|
||||
free(ovf);
|
||||
(*env)->SetObjectField(env, nvf, nvf_field_ovf, NULL);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#include "com_jme3_texture_plugins_AndroidNativeImageLoader.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifndef NDEBUG
|
||||
#include <android/log.h>
|
||||
#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, \
|
||||
"NativeImageLoader", fmt, ##__VA_ARGS__);
|
||||
@ -12,7 +12,7 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_NO_STDIO
|
||||
#define STBI_NO_HDR
|
||||
#include "stb_image.h"
|
||||
#include "STBI/stb_image.h"
|
||||
|
||||
typedef struct
|
||||
{
|
@ -1,16 +0,0 @@
|
||||
TARGET_PLATFORM := android-9
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := stbijme
|
||||
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)
|
||||
|
||||
LOCAL_CFLAGS := -std=c99
|
||||
LOCAL_LDLIBS := -lz -llog -Wl,-s
|
||||
|
||||
LOCAL_SRC_FILES := com_jme3_texture_plugins_AndroidNativeImageLoader.c
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
@ -1,3 +0,0 @@
|
||||
APP_PLATFORM := android-9
|
||||
APP_OPTIM := release
|
||||
APP_ABI := all
|
@ -1,123 +0,0 @@
|
||||
// stb_image url for download
|
||||
String stbiUrl = 'https://raw.githubusercontent.com/nothings/stb/master/stb_image.h'
|
||||
String stbiDownloadTarget = 'stb_image.h'
|
||||
|
||||
// stb_image is not downloaded. The single source file is included in the repo
|
||||
String stbiFolder = 'stb_image'
|
||||
|
||||
//Working directories for the ndk build.
|
||||
String stbiBuildDir = "${buildDir}" + File.separator + 'stb_image'
|
||||
String stbiBuildJniDir = stbiBuildDir + File.separator + 'jni'
|
||||
String stbiBuildLibsDir = stbiBuildDir + File.separator + 'libs'
|
||||
|
||||
//Pre-compiled libs directory
|
||||
String stbiPreCompiledLibsDir = 'libs' + File.separator + 'stb_image'
|
||||
|
||||
// jME Android Native source files path
|
||||
String stbiJmeAndroidPath = 'src/native/jme_stbi'
|
||||
|
||||
// Download external source files if not available
|
||||
task downloadStbImage(type: MyDownload) {
|
||||
sourceUrl = stbiUrl
|
||||
target = file(stbiFolder + File.separator + stbiDownloadTarget)
|
||||
}
|
||||
|
||||
// Copy stb_image.h to the source directory.
|
||||
task copyStbiFiles(type: Copy) {
|
||||
def sourceDir = file(stbiFolder)
|
||||
def outputDir = file(stbiJmeAndroidPath)
|
||||
// println "copyStbiFiles sourceDir: " + sourceDir
|
||||
// println "copyStbiFiles outputDir: " + outputDir
|
||||
|
||||
from sourceDir
|
||||
into outputDir
|
||||
}
|
||||
copyStbiFiles.dependsOn {
|
||||
def stbiFilePath = project.projectDir.absolutePath + stbiFolder + File.separator + stbiDownloadTarget
|
||||
def stbiFile = new File(stbiFilePath)
|
||||
// println "zipFile path: " + zipFile.absolutePath
|
||||
// println "zipFile exists: " + zipFile.exists()
|
||||
if (!stbiFile.exists()) {
|
||||
downloadStbImage
|
||||
}
|
||||
}
|
||||
|
||||
task generateStbiHeaders(type: Exec) {
|
||||
String destDirPath = stbiJmeAndroidPath
|
||||
String classes = ""
|
||||
.concat("com.jme3.texture.plugins.AndroidNativeImageLoader, ")
|
||||
|
||||
// println "stb_image classes = " + classes
|
||||
// println "stb_image destDir = " + destDir
|
||||
// println "stb_image classpath = " + project.projectClassPath
|
||||
|
||||
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
|
||||
args '-d', destDirPath
|
||||
args '-classpath', project.projectClassPath
|
||||
args "com.jme3.texture.plugins.AndroidNativeImageLoader"
|
||||
}
|
||||
|
||||
// Copy jME Android native files to jni directory
|
||||
task copyStbiJmeFiles(type: Copy, dependsOn:[copyStbiFiles, generateStbiHeaders]) {
|
||||
def sourceDir = file(stbiJmeAndroidPath)
|
||||
def outputDir = file(stbiBuildJniDir)
|
||||
// println "copyStbiJmeFiles sourceDir: " + sourceDir
|
||||
// println "copyStbiJmeFiles outputDir: " + outputDir
|
||||
|
||||
from sourceDir
|
||||
into outputDir
|
||||
}
|
||||
|
||||
task buildStbiNativeLib(type: Exec, dependsOn: copyStbiJmeFiles) {
|
||||
// println "stb_image build dir: " + stbiBuildDir
|
||||
// println "ndkCommandPath: " + rootProject.ndkCommandPath
|
||||
|
||||
workingDir stbiBuildDir
|
||||
executable rootProject.ndkCommandPath
|
||||
args '-j8'
|
||||
}
|
||||
|
||||
task updatePreCompiledStbiLibs(type: Copy, dependsOn: buildStbiNativeLib) {
|
||||
def sourceDir = new File(stbiBuildLibsDir)
|
||||
def outputDir = new File(stbiPreCompiledLibsDir)
|
||||
// println "updatePreCompiledStbiLibs sourceDir: " + sourceDir
|
||||
// println "updatePreCompiledStbiLibs outputDir: " + outputDir
|
||||
|
||||
from sourceDir
|
||||
into outputDir
|
||||
}
|
||||
|
||||
// Copy pre-compiled libs to build directory (when not building new libs)
|
||||
task copyPreCompiledStbiLibs(type: Copy) {
|
||||
def sourceDir = file(stbiPreCompiledLibsDir)
|
||||
def outputDir = file(stbiBuildLibsDir)
|
||||
// println "copyStbiJmeFiles sourceDir: " + sourceDir
|
||||
// println "copyStbiJmeFiles outputDir: " + outputDir
|
||||
|
||||
from sourceDir
|
||||
into outputDir
|
||||
}
|
||||
|
||||
if (rootProject.ndkExists) {
|
||||
// build native libs and update stored pre-compiled libs to commit
|
||||
compileJava.dependsOn { updatePreCompiledStbiLibs }
|
||||
} else {
|
||||
// use pre-compiled native libs (not building new ones)
|
||||
compileJava.dependsOn { copyPreCompiledStbiLibs }
|
||||
}
|
||||
|
||||
jar.into("lib") { from stbiBuildLibsDir }
|
||||
|
||||
// Helper class to wrap ant dowload task
|
||||
class MyDownload extends DefaultTask {
|
||||
@Input
|
||||
String sourceUrl
|
||||
|
||||
@OutputFile
|
||||
File target
|
||||
|
||||
@TaskAction
|
||||
void download() {
|
||||
ant.get(src: sourceUrl, dest: target)
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.jme3.audio.plugins;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class NativeVorbisFile {
|
||||
|
||||
public int fd;
|
||||
public ByteBuffer ovf;
|
||||
public boolean seekable;
|
||||
public int channels;
|
||||
public int sampleRate;
|
||||
public int bitRate;
|
||||
public int totalBytes;
|
||||
public float duration;
|
||||
|
||||
static {
|
||||
System.loadLibrary("decodejme");
|
||||
nativeInit();
|
||||
}
|
||||
|
||||
public NativeVorbisFile(int fd) throws IOException {
|
||||
open(fd);
|
||||
}
|
||||
|
||||
private native void open(int fd) throws IOException;
|
||||
|
||||
public native void seekTime(double time) throws IOException;
|
||||
|
||||
public native int read(byte[] buf, int off, int len) throws IOException;
|
||||
|
||||
public native void readFully(ByteBuffer out) throws IOException;
|
||||
|
||||
public native void close();
|
||||
|
||||
public static native void nativeInit();
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.jme3.audio.plugins;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import com.jme3.asset.AssetInfo;
|
||||
import com.jme3.asset.AssetLoader;
|
||||
import com.jme3.asset.plugins.AndroidLocator;
|
||||
import com.jme3.asset.plugins.AndroidLocator.AndroidAssetInfo;
|
||||
import com.jme3.audio.AudioBuffer;
|
||||
import com.jme3.audio.AudioKey;
|
||||
import com.jme3.audio.SeekableStream;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class NativeVorbisLoader implements AssetLoader {
|
||||
|
||||
private static class VorbisInputStream extends InputStream implements SeekableStream {
|
||||
|
||||
private final NativeVorbisFile file;
|
||||
|
||||
public VorbisInputStream(NativeVorbisFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buf) throws IOException {
|
||||
return file.read(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buf, int off, int len) throws IOException {
|
||||
return file.read(buf, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
throw new IOException("Not supported for audio streams");
|
||||
}
|
||||
|
||||
public void setTime(float time) {
|
||||
try {
|
||||
file.seekTime(time);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(AssetInfo assetInfo) throws IOException {
|
||||
AudioKey key = (AudioKey) assetInfo.getKey();
|
||||
if (!(assetInfo instanceof AndroidLocator.AndroidAssetInfo)) {
|
||||
throw new UnsupportedOperationException("Cannot load audio files from classpath." +
|
||||
"Place your audio files in " +
|
||||
"Android's assets directory");
|
||||
}
|
||||
|
||||
AndroidAssetInfo aai = (AndroidAssetInfo) assetInfo;
|
||||
AssetFileDescriptor afd = aai.openFileDescriptor();
|
||||
int fd = afd.getParcelFileDescriptor().getFd();
|
||||
|
||||
NativeVorbisFile file = new NativeVorbisFile(fd);
|
||||
ByteBuffer data = BufferUtils.createByteBuffer(file.totalBytes);
|
||||
file.readFully(data);
|
||||
AudioBuffer ab = new AudioBuffer();
|
||||
ab.setupFormat(file.channels, 16, file.sampleRate);
|
||||
ab.updateData(data);
|
||||
return ab;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user