How to play audio using Android NDK?

1,332 views
Skip to first unread message

u_r_man

unread,
Oct 5, 2010, 7:42:43 AM10/5/10
to android-ndk
Are there any methods to play audio files using c/c++ NOT JAVA CODE?

mic _

unread,
Oct 5, 2010, 9:23:35 AM10/5/10
to andro...@googlegroups.com
No, at the moment there are no APIs provided for this in the NDK.

/Michael

On Tue, Oct 5, 2010 at 1:42 PM, u_r_man <urman.ra...@gmail.com> wrote:
Are there any methods to play audio files using c/c++ NOT JAVA CODE?

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.


Tim Mensch

unread,
Oct 5, 2010, 1:30:03 PM10/5/10
to andro...@googlegroups.com
On 10/5/2010 5:42 AM, u_r_man wrote:
> Are there any methods to play audio files using c/c++ NOT JAVA CODE?
Issue 3434:

http://code.google.com/p/android/issues/detail?id=3434

Star the issue if you want to be notified whether Google ever takes
notice. At the moment it doesn't even have an owner, and so I assume no
one at Google cares. :(

This is a symptom of a pretty major problem: Many issues in issue
tracker seem to be neglected or (apparently) ignored, not just for days
or weeks, but many months. This issue, for example, doesn't have any
apparent attention from Google since Sept of 2009, despite being a
rather crucial missing feature for Android. Developers would appreciate
a bit more transparency, so we'd know that someone at Google is at least
AWARE that this critical issue has been sitting around for over a year,
though what we'd really like, of course, is a complete API so Android
can actually support interactive music apps, which it currently can't.

Tim

Glenn Kasten

unread,
Oct 5, 2010, 1:56:50 PM10/5/10
to android-ndk
At Google I/O 2010, Google announced the intention to include native
audio APIs based on Khronos Group OpenSL ES in an upcoming release.
OpenSL ES is described at http://www.khronos.org/opensles/

Doug Schaefer

unread,
Oct 5, 2010, 2:06:42 PM10/5/10
to andro...@googlegroups.com
That had been mentioned in an early post to this group. Glad to hear
the idea is still alive. Crossing my fingers for Android 3.0...

David Whittaker

unread,
Oct 5, 2010, 2:18:12 PM10/5/10
to andro...@googlegroups.com

The best solution currently available is to write a c++ wrapper that mimics the Java AudioTrack class.  In the constructor, cache the method IDs in private variables, and in each method, just do the JNI work to convert to Java, and call the Java methods.  This is an instance of the Adapter design pattern.  You can even alter the interface to keep track of more information, like a timestamp for when play was called, so that you can always get at the current position of the actual playback head (getPlaybackHeadPosition just gives you the amount of data you've written) with a little math ((current time - play time) * samples per second), and hope that it has maintained realtime.  You can add some more helper functions to simplify the C++ interface.

Then you end up with a nice class that you can call from c++ code, without having to scatter all that JNI throughout your code.  Still not the greatest solution, but the best available.

On Oct 5, 2010 7:57 AM, "u_r_man" <urman.ra...@gmail.com> wrote:
> Are there any methods to play audio files using c/c++ NOT JAVA CODE?
>

Olivier Guilyardi

unread,
Oct 5, 2010, 2:18:46 PM10/5/10
to andro...@googlegroups.com
Well, have you browsed the OpenSL spec? I quite don't get why it's full of bells
and whistles but doesn't mention simple audio I/O.

Ngo Van Luyen

unread,
Oct 6, 2010, 8:09:18 AM10/6/10
to andro...@googlegroups.com

Ngo Van Luyen

unread,
Oct 8, 2010, 7:35:32 AM10/8/10
to andro...@googlegroups.com
Download alsa-lib from android source, we can see in test folder, something likes:

err = snd_rawmidi_open(NULL,&handle_out,device_out,0);



2010/10/6 Ngo Van Luyen <nvlu...@gmail.com>

Ngo Van Luyen

unread,
Oct 8, 2010, 8:05:15 AM10/8/10
to andro...@googlegroups.com
it should be interesting to check frameworks/base/media/libmedia/AudioTrack.cpp

I'm trying to find a way to play audio from native code too :), and avoid "big modification" in next Android version



2010/10/8 Ngo Van Luyen <nvlu...@gmail.com>

Kazuki Sakamoto

unread,
Oct 8, 2010, 8:40:35 AM10/8/10
to andro...@googlegroups.com
On Wed, Oct 6, 2010 at 3:18 AM, David Whittaker <dpwhi...@gmail.com> wrote:
> The best solution currently available is to write a c++ wrapper that mimics
> the Java AudioTrack class.

I found it.

http://repo.or.cz/w/openal-soft/android.git/blob/HEAD:/Alc/android.c

It is a part of OpenAL software implementation for Android.

http://repo.or.cz/w/openal-soft/android.git

sakamoto

Ngo Van Luyen

unread,
Oct 13, 2010, 11:54:37 AM10/13/10
to andro...@googlegroups.com
Hi,

Does it work? I am not familiar with JNI so it still does not work here :(

I'm trying ....


2010/10/8 Kazuki Sakamoto <saka...@splhack.org>
--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.

Gauthier B

unread,
Oct 13, 2010, 1:23:35 PM10/13/10
to andro...@googlegroups.com
Hi,

Yes it works, I just did it.

I tried multithreaded and singlethread-way and both are functionnal.

When you're working with Jni don't forget to get the right JNIEnv *.

The OpenAL implementation seems ok, I did quite the same.

Gauthier

Ngo Van Luyen

unread,
Oct 13, 2010, 1:58:59 PM10/13/10
to andro...@googlegroups.com
cool, me also, I did the same, but I have the problem with JNI:

//-------------------------------------------------
jint JNI_OnLoad(JavaVM* vm, void* reserved){
snd_obj = new CAndroidSnd();
snd_obj->setjavaVM(vm);
     //javaVM = vm;
     return JNI_VERSION_1_2;
}
//---------------------------------------------------------
CAndroidSnd::CAndroidSnd(){
javaVM = NULL;
mtrack = NULL;
cAudioTrack = NULL;
}
//---------------------------------------------------------
JNIEnv* CAndroidSnd::GetEnv(){
    JNIEnv* env = NULL;
    if (javaVM) (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_2);
    return env;
}
............................
............................
............................
............................
//-------------------------------------------------

But I got error: 

.../jni/android_snd.cpp: In member function 'JNIEnv* CAndroidSnd::GetEnv()':
.../jni/android_snd.cpp:27: error: base operand of '->' has non-pointer type '_JavaVM'


Can you share your code?

Thanks,


2010/10/13 Gauthier B <gogo...@gmail.com>

Gauthier B

unread,
Oct 13, 2010, 4:17:34 PM10/13/10
to andro...@googlegroups.com
I can't share my code but i can help you a little bit;
When you use Jni in a c++ way, you can do the following :
javaVM->GetEnv( ... )

Try this but the arguments can change.

Also, I didn't use the JavaVM pointer because I have a JniEnv pointer from my native function called in Java.

Gauthier

David Whittaker

unread,
Oct 13, 2010, 5:13:58 PM10/13/10
to andro...@googlegroups.com
Also, you don't give the javaVM as the first parameter in C++ either.
So it should be something like:

javaVM->GetEnv((void**)&env, JNI_VERSION_1_2);

Note, I haven't tried this, but that's the convention.

Ngo Van Luyen

unread,
Oct 13, 2010, 5:32:14 PM10/13/10
to andro...@googlegroups.com
So why don't you use directly java code?

My problem is: I have libmusic which will decode and play some music, it's cross platform. And some platform-dependent lib android_snd (for Android), symbian_snd (for Symbian), wince_snd ( for Windows Mobile ) ...
libmusic will decode, then load sound lib (android_snd, symbian_snd, wince_snd) to play music. That why I don't have JNIEnv pointer. And I try to get it from JNI_OnLoad

Gauthier B

unread,
Oct 13, 2010, 5:44:50 PM10/13/10
to andro...@googlegroups.com
Because I'm porting some stuff too.

But in your case you just have to do like we said :


JNIEnv* CAndroidSnd::GetEnv(){
    JNIEnv* env = NULL;
    if (javaVM) javaVM->GetEnv( (void**)&env, JNI_VERSION_1_2 );
    return env;
}

The rest seems ok.

Gauthier

Ngo Van Luyen

unread,
Oct 14, 2010, 9:03:56 AM10/14/10
to andro...@googlegroups.com
Hi,

I still get crash when call 'write' to play audio, here is my code to test:

Header file
//----------------------------------------------------------------------------------------------------------------------------------------
class CAndroidSnd{

private:
JavaVM* javaVM;
jobject mtrack;
jclass cAudioTrack;
jclass clAudioTrack;

jmethodID mAudioTrack;
jmethodID mGetMinBufferSize;
jmethodID mPlay;
jmethodID mStop;
jmethodID mRelease;
jmethodID mWrite;

int mbufferSizeInBytes;

FILE *debug_f;
void debug_log(const char* msg);
void debug_log(const int n);
private:
JNIEnv* GetEnv();

public:
CAndroidSnd();
~CAndroidSnd();
void setjavaVM(JavaVM* vm);
int nativesound_open();
void nativesound_write(jshortArray array, jint len);
void nativesound_writetest();
void nativesound_close();
};
//----------------------------------------------------------------------------------------------------------------------------------------

CPP file
//----------------------------------------------------------------------------------------------------------------------------------------

#include "android_snd.h"

//---------------------------------------------------------
jint JNI_OnLoad(JavaVM* vm, void* reserved){
snd_obj = new CAndroidSnd();
snd_obj->setjavaVM(vm);
     return JNI_VERSION_1_2;
}
//---------------------------------------------------------
CAndroidSnd::CAndroidSnd(){
javaVM = NULL;
mtrack = NULL;
cAudioTrack = NULL;

debug_f = fopen(DEBUG_FILE, "w");
debug_log("CAndroidSnd::CAndroidSnd");
}
//---------------------------------------------------------
CAndroidSnd::~CAndroidSnd(){
debug_log("CAndroidSnd::~CAndroidSnd\n");
fclose(debug_f);
}
//---------------------------------------------------------
void CAndroidSnd::setjavaVM(JavaVM* vm){
javaVM = vm;
}
//---------------------------------------------------------
JNIEnv* CAndroidSnd::GetEnv(){
     JNIEnv* env = NULL;
if (javaVM) {
jint res = javaVM->GetEnv((void**)&env, JNI_VERSION_1_2);
debug_log("res="); debug_log((int)res);
}
else debug_log("javaVM = NULL");
     return env;
}
//---------------------------------------------------------
int CAndroidSnd::nativesound_open(){
JNIEnv* env = GetEnv();
    int channels;
    int bytes;

    if (!cAudioTrack)
    {
        /* Cache AudioTrack class and it's method id's
         * And do this only once!
         */

cAudioTrack = env->FindClass("android/media/AudioTrack");
        if (!cAudioTrack) {
debug_log("cAudioTrack = NULL");
             return 1;
        }

mAudioTrack = env->GetMethodID(cAudioTrack, "<init>", "(IIIIII)V");
        mGetMinBufferSize = env->GetStaticMethodID(cAudioTrack, "getMinBufferSize", "(III)I");
        mPlay = env->GetMethodID(cAudioTrack, "play", "()V");
        mStop = env->GetMethodID(cAudioTrack, "stop", "()V");
        mRelease = env->GetMethodID(cAudioTrack, "release", "()V");
        mWrite = env->GetMethodID(cAudioTrack, "write", "([BII)I");
    }
javaVM->AttachCurrentThread(&env, NULL);

env->PushLocalFrame(2);

int sampleRateInHz = 44100;
int channelConfig = CHANNEL_OUT_MONO; //AudioFormat.CHANNEL_OUT_MONO
int audioFormat = ENCODING_PCM_16BIT; //AudioFormat.ENCODING_PCM_16BIT

mbufferSizeInBytes = env->CallStaticIntMethod(cAudioTrack, mGetMinBufferSize, sampleRateInHz, channelConfig, audioFormat);
debug_log("mbufferSizeInBytes=");
debug_log(mbufferSizeInBytes);

mtrack = env->NewObject(cAudioTrack, mAudioTrack, STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, mbufferSizeInBytes, MODE_STREAM); //AudioTrack.MODE_STREAM
if (mtrack == NULL) debug_log("mtrack=NULL");
else env->CallNonvirtualVoidMethod(mtrack, cAudioTrack, mPlay);

    return 0;
}
//---------------------------------------------------------
void CAndroidSnd::nativesound_writetest(){
debug_log("nativesound_writetest");

JNIEnv* env = GetEnv();

float frequency = 440;
        short *samples = new short[1024];
        float angle = 0;
        float increment = (float)(2*3.14) * frequency / 44100;
        
        for( int i = 0; i < 1024; i++ ) {
           samples[i] = (short)sin( angle );
           angle += increment;
        }
env->CallNonvirtualIntMethod(mtrack, cAudioTrack, mWrite, samples, 0, 1024);

delete samples;
}
//---------------------------------------------------------
void CAndroidSnd::nativesound_close(){
JNIEnv* env = GetEnv();

env->CallNonvirtualVoidMethod(mtrack, cAudioTrack, mStop);
env->CallNonvirtualVoidMethod(mtrack, cAudioTrack, mRelease);

env->PopLocalFrame(NULL);

javaVM->DetachCurrentThread();
}
//---------------------------------------------------------
void CAndroidSnd::debug_log(const char* msg){
fprintf(debug_f, "%s\n", msg);
fflush(debug_f);
}
//---------------------------------------------------------
void CAndroidSnd::debug_log(const int n){
fprintf(debug_f, "int = %d\n", n);
fflush(debug_f);
}
//----------------------------------------------------------------------------------------------------------------------------------------


2010/10/13 Gauthier B <gogo...@gmail.com>

Ngo Van Luyen

unread,
Oct 14, 2010, 9:05:18 AM10/14/10
to andro...@googlegroups.com
Here is the logcat:


D/dalvikvm(  705): Trying to load lib /data/data/com.abc.NativeSound/lib/libandroid_snd.so 0x43b87980
D/dalvikvm(  705): Added shared lib /data/data/com.abc.NativeSound/lib/libandroid_snd.so 0x43b87980
D/dalvikvm(  705): +++ not scanning '/system/lib/libwebcore.so' for 'init' (wrong CL)
D/dalvikvm(  705): +++ not scanning '/system/lib/libmedia_jni.so' for 'init' (wrong CL)
D/dalvikvm(  705): +++ not scanning '/system/lib/libexif.so' for 'init' (wrong CL)
D/dalvikvm(  705): +++ not scanning '/system/lib/libwebcore.so' for 'open' (wrong CL)
D/dalvikvm(  705): +++ not scanning '/system/lib/libmedia_jni.so' for 'open' (wrong CL)
D/dalvikvm(  705): +++ not scanning '/system/lib/libexif.so' for 'open' (wrong CL)
D/dalvikvm(  705): +++ not scanning '/system/lib/libwebcore.so' for 'write' (wrong CL)
D/dalvikvm(  705): +++ not scanning '/system/lib/libmedia_jni.so' for 'write' (wrong CL)
D/dalvikvm(  705): +++ not scanning '/system/lib/libexif.so' for 'write' (wrong CL)
W/dalvikvm(  705): JNI WARNING: 0x43b902f0 is not a valid JNI reference
W/dalvikvm(  705):              in Lcom/vanluyen/NativeSound/SndObject;.write ([SI)V (CallNonvirtualIntMethodV)
I/dalvikvm(  705): "main" prio=5 tid=3 RUNNABLE
I/dalvikvm(  705):   | group="main" sCount=0 dsCount=0 s=N obj=0x4001b268 self=0xbd00
I/dalvikvm(  705):   | sysTid=705 nice=0 sched=0/0 cgrp=default handle=-1344001384
I/dalvikvm(  705):   at com.vanluyen.NativeSound.SndObject.write(Native Method)
I/dalvikvm(  705):   at com.vanluyen.NativeSound.NativeSound.onCreate(NativeSound.java:28)
I/dalvikvm(  705):   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
I/dalvikvm(  705):   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
I/dalvikvm(  705):   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
I/dalvikvm(  705):   at android.app.ActivityThread.access$2200(ActivityThread.java:119)
I/dalvikvm(  705):   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
I/dalvikvm(  705):   at android.os.Handler.dispatchMessage(Handler.java:99)
I/dalvikvm(  705):   at android.os.Looper.loop(Looper.java:123)
I/dalvikvm(  705):   at android.app.ActivityThread.main(ActivityThread.java:4363)
I/dalvikvm(  705):   at java.lang.reflect.Method.invokeNative(Native Method)
I/dalvikvm(  705):   at java.lang.reflect.Method.invoke(Method.java:521)
I/dalvikvm(  705):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
I/dalvikvm(  705):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
I/dalvikvm(  705):   at dalvik.system.NativeStart.main(Native Method)



2010/10/14 Ngo Van Luyen <nvlu...@gmail.com>

Gauthier B

unread,
Oct 14, 2010, 10:05:12 AM10/14/10
to andro...@googlegroups.com
You have to keep a global reference to your mtrack object :

mtrack  = env->NewGlobalRef( mtrack );

Check how they did in OpenAL ;)

Gauthier

Ngo Van Luyen

unread,
Oct 21, 2010, 7:55:04 AM10/21/10
to andro...@googlegroups.com
Thanks,

My problem now is:

I have 2 libs: libA and libB. libA will be loaded by Java code. LibB will be loaded by libA. So in libB, I don't have JNI_OnLoad and I cannot obtain the pointer to JavaVM. So when loading libA, in JNI_OnLoad, I save JavaVM pointer to a variable.
When a function in libA calling a function in libB, I pass the JavaVM pointer. But when calling javaVM->GetEnv((void**)&env, JNI_VERSION_1_2); in libB, env == NULL.

Do you know why?

Thanks,


2010/10/14 Gauthier B <gogo...@gmail.com>
Reply all
Reply to author
Forward
0 new messages