* 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 changesexperimental
parent
603432790b
commit
5f0c2035c1
Binary file not shown.
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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) |
@ -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,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…
Reference in new issue