* 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