Moving Forward

Homepage of Andrew Robinson

A JNI Wrapper for Speex on Android

with 11 comments

After reading this article on installing Speex for Android I realized that it was severely lacking. The author never presents the JNI interface, and some of the ndk-build flags are incorrect for my platform.

Here’s a set of steps to setup Speex for Android, and building a simple wrapper, assuming you have the Android NDK installed. If you don’t have the NDK installed, download it from the Android homepage and add the bin directory to your path statement.

  1. Download the latest Speex source tarball from the downloads page.
  2. Extract the files and copy the include and libspeex directories into your JNI directory in a current Android project.
  3. Create a file called native.c and add the following code listing to it, this is the missing JNI interface:
    #include <jni.h>
    #include "speex/speex.h"
    
    #define FRAME_SIZE 320
    
    int nbBytes;
    /*Holds the state of the encoder*/
    void *state;
    /*Holds bits so they can be read and written to by the Speex routines*/
    SpeexBits bits;
    int i, tmp;
    
    void Java_fusao_awesome_TestAppActivity_init(JNIEnv * env, jobject jobj) {
       /*Create a new encoder state in narrowband mode*/
       state = speex_encoder_init(&speex_wb_mode);
    
       /*Set the quality to 8*/
       tmp=8;
       speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp);
    
       /*Initialization of the structure that holds the bits*/
       speex_bits_init(&bits);
    }
    
    jbyteArray Java_fusao_awesome_TestAppActivity_encode(JNIEnv * env, jobject jobj, jshortArray inputData) {
    		jbyteArray ret;
    
    		jshort * inputArrayElements = (*env)->GetShortArrayElements(env, inputData, 0);
    
    		/*Flush all the bits in the struct so we can encode a new frame*/
    		speex_bits_reset(&bits);
    
    		/*Encode the frame*/
    		speex_encode_int(state, inputArrayElements, &bits);
    		/*Copy the bits to an array of char that can be written*/
    		nbBytes = speex_bits_nbytes(&bits);
    
    		ret = (jbyteArray) ((*env)->NewByteArray(env, nbBytes));
    		jbyte * arrayElements = (*env)->GetByteArrayElements(env, ret, 0);
    
    		speex_bits_write(&bits, arrayElements, nbBytes);
    
    		(*env)->ReleaseShortArrayElements(env, inputData, inputArrayElements, JNI_ABORT);
    		(*env)->ReleaseByteArrayElements(env, ret, arrayElements, 0);
    		return ret;
    }
    

    Notice that the function names need to be modified to fit your project. In this case I was working in the fusao.awesome namespace, building an activity called TestAppActivity, modify your code to match what you’re doing.

    JNI is not an Android-specific technology, there are plenty of JavaDoc and Oracle resources available for writing the JNI interfaces. At first it’s not intuitive, but the documentation should help you to write your own interfaces. I’ve listed some additional resources at the end of this post for further customization.

    I’ve implemented a really simple encode function, and initializer. The encode function simply takes a frame (20ms at 16kHz) of audio, and encodes it using the default wideband options. It should easily be modified to accommodate additional functions, such as preprocessing to remove non-voice data.

  4. Create a file called Android.mk, to handle the build configuration. Paste in the following configuration:
    
    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE    := libspeex
    LOCAL_CFLAGS = -DFIXED_POINT -DUSE_KISS_FFT -DEXPORT="" -UHAVE_CONFIG_H
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
    
    LOCAL_SRC_FILES :=  \
    ./libspeex/bits.c \
    ./libspeex/buffer.c \
    ./libspeex/cb_search.c \
    ./libspeex/exc_10_16_table.c \
    ./libspeex/exc_10_32_table.c \
    ./libspeex/exc_20_32_table.c \
    ./libspeex/exc_5_256_table.c \
    ./libspeex/exc_5_64_table.c \
    ./libspeex/exc_8_128_table.c \
    ./libspeex/fftwrap.c \
    ./libspeex/filterbank.c \
    ./libspeex/filters.c \
    ./libspeex/gain_table.c \
    ./libspeex/gain_table_lbr.c \
    ./libspeex/hexc_10_32_table.c \
    ./libspeex/hexc_table.c \
    ./libspeex/high_lsp_tables.c \
    ./libspeex/jitter.c \
    ./libspeex/kiss_fft.c \
    ./libspeex/kiss_fftr.c \
    ./libspeex/lpc.c \
    ./libspeex/lsp.c \
    ./libspeex/lsp_tables_nb.c \
    ./libspeex/ltp.c \
    ./libspeex/mdf.c \
    ./libspeex/modes.c \
    ./libspeex/modes_wb.c \
    ./libspeex/nb_celp.c \
    ./libspeex/preprocess.c \
    ./libspeex/quant_lsp.c \
    ./libspeex/resample.c \
    ./libspeex/sb_celp.c \
    ./libspeex/scal.c \
    ./libspeex/smallft.c \
    ./libspeex/speex.c \
    ./libspeex/speex_callbacks.c \
    ./libspeex/speex_header.c \
    ./libspeex/stereo.c \
    ./libspeex/vbr.c \
    ./libspeex/vq.c \
    ./libspeex/window.c \
    ./native.c
    
    include $(BUILD_SHARED_LIBRARY)
    
  5. Open up jni/include/speex/speex_config_types.h (create it if not already present) and add the following bit:
    #ifndef __SPEEX_TYPES_H__
    #define __SPEEX_TYPES_H__
    typedef short spx_int16_t;
    typedef unsigned short spx_uint16_t;
    typedef int spx_int32_t;
    typedef unsigned int spx_uint32_t;
    #endif
    
  6. In a command line of the main directory of your project run ndk-build, ensure that it completes successfully.
  7. In the activity you’re creating add the following framework inside the class to tell Java what the function definition looks like for your native interface:
    	native byte[] encode(short [] inputData);
    	native void init();
    
    	static {
    		System.loadLibrary("speex");
    	}
    
  8. Ensure you build your project again, now you should be able to envoke your JNI functions:
        	short [] inputArray = new short[320];
            // Write data to inputArray.
    	byte [] encodedBuffer = encode(inputArray);
    

And that’s all there is to it!

Additional Resources
JavaDocs on Array JNI Function Specifications
A JNI Example from Oracle
A nice tutorial on building a default JNI applicatoin

Next time I’ll be presenting a complete code listening that demonstrates taking this interface, wrapping it with Google ProtoBuffers, sending it over a socket, and reconstructing the Speex packets on a server to play back on a personal computer!

Written by Andrew Robinson

November 28th, 2011 at 3:10 am

Posted in Uncategorized

11 Responses to 'A JNI Wrapper for Speex on Android'

Subscribe to comments with RSS or TrackBack to 'A JNI Wrapper for Speex on Android'.

  1. I am trying to do decoding of speex encoded byte array can you please help me through it?

    Hemant

    27 Jan 12 at 8:46 am

  2. [...] native interface). I am searching for jni wrapper for speex. I found a very good tutorial here http://andrewbrobinson.com/2011/11/28/a-jni-wrapper-for-speex-on-android/. it provides encoding using the [...]

  3. That is my own question on stack overflow but still did not got any reply.

    Hemant

    30 Jan 12 at 7:10 am

  4. [...] have tried Android-ndk and got encoding done, but getting a problem in decoding the byte array. Is there any other alternative to achieve [...]

  5. That is also my own question on stack overflow but still did not got any reply.

    Hemant

    2 Feb 12 at 6:26 am

  6. [...] native interface). I am searching for jni wrapper for speex. I found a very good tutorial here http://andrewbrobinson.com/2011/11/28/a-jni-wrapper-for-speex-on-android/. it provides encoding using the [...]

  7. I’m impressed, I must say. Actually not often do I encounter a blog that’s each educative and entertaining, and let me tell you, you might have hit the nail on the head. Your idea is excellent; the problem is something that not sufficient persons are speaking intelligently about. I am very blissful that I stumbled across this in my seek for something regarding this.

    Terisa Lupardus

    16 Apr 12 at 2:21 pm

  8. this implementation did not work for me.It runs fine, but the sounds is not clear, seems an issue with the encoding procedure.

    chamini

    28 May 12 at 6:48 am

  9. Hi chamini i have found the way to do it and left that for future implementation here is the code for encoding/decoding it may help you..

    http://stackoverflow.com/questions/9092415/speex-support-in-android

    Hemant

    28 May 12 at 6:56 am

  10. thanx Hemant

    chamini

    1 Jun 12 at 5:42 am

  11. Great post! Usefull
    I did the same couple years ago and now I am facing other problem on ICS
    looks like for x86 ICS android sppexwrapper library is missing. Do you know interface for speexwrapper library?

    It should be one you described but more extended.

    looks like digging into ISC sources is only way to discover this. Any help will be appreciated.
    this is what I am looking for
    >>Caused by: java.lang.UnsatisfiedLinkError: Couldn’t load speexwrapper: findLibrary returned null

    Thanks for post again!

    Oleksandr

    22 Oct 12 at 3:13 am

Leave a Reply