Update native-audio example app

Additions:
 - volume
 - stereo position
 - mute / solo
 - explicit pause / play / loop buttons
 - spinner for URI strings

Change-Id: I92f3a191f715567531d03998d4b80e32cc9aceed
diff --git a/ndk/platforms/android-9/samples/native-audio/jni/native-audio-jni.c b/ndk/platforms/android-9/samples/native-audio/jni/native-audio-jni.c
index bc1ad85..105c64a 100644
--- a/ndk/platforms/android-9/samples/native-audio/jni/native-audio-jni.c
+++ b/ndk/platforms/android-9/samples/native-audio/jni/native-audio-jni.c
@@ -30,7 +30,7 @@
 
 // for native audio
 #include <SLES/OpenSLES.h>
-#include "SLES/OpenSLES_Android.h"
+#include <SLES/OpenSLES_Android.h>
 
 // for native asset manager
 #include <sys/types.h>
@@ -60,6 +60,8 @@
 static SLPlayItf bqPlayerPlay;
 static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
 static SLEffectSendItf bqPlayerEffectSend;
+static SLMuteSoloItf bqPlayerMuteSolo;
+static SLVolumeItf bqPlayerVolume;
 
 // aux effect on the output mix, used by the buffer queue player
 static const SLEnvironmentalReverbSettings reverbSettings =
@@ -69,11 +71,15 @@
 static SLObjectItf uriPlayerObject = NULL;
 static SLPlayItf uriPlayerPlay;
 static SLSeekItf uriPlayerSeek;
+static SLMuteSoloItf uriPlayerMuteSolo;
+static SLVolumeItf uriPlayerVolume;
 
 // file descriptor player interfaces
 static SLObjectItf fdPlayerObject = NULL;
 static SLPlayItf fdPlayerPlay;
 static SLSeekItf fdPlayerSeek;
+static SLMuteSoloItf fdPlayerMuteSolo;
+static SLVolumeItf fdPlayerVolume;
 
 // recorder interfaces
 static SLObjectItf recorderObject = NULL;
@@ -199,10 +205,12 @@
     SLDataSink audioSnk = {&loc_outmix, NULL};
 
     // create audio player
-    const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND};
-    const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+    const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND,
+            /*SL_IID_MUTESOLO,*/ SL_IID_VOLUME};
+    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
+            /*SL_BOOLEAN_TRUE,*/ SL_BOOLEAN_TRUE};
     result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
-            2, ids, req);
+            3, ids, req);
     assert(SL_RESULT_SUCCESS == result);
 
     // realize the player
@@ -227,6 +235,16 @@
             &bqPlayerEffectSend);
     assert(SL_RESULT_SUCCESS == result);
 
+#if 0   // mute/solo is not supported for sources that are known to be mono, as this is
+    // get the mute/solo interface
+    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);
+    assert(SL_RESULT_SUCCESS == result);
+#endif
+
+    // get the volume interface
+    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
+    assert(SL_RESULT_SUCCESS == result);
+
     // set the player's state to playing
     result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
     assert(SL_RESULT_SUCCESS == result);
@@ -255,10 +273,10 @@
     SLDataSink audioSnk = {&loc_outmix, NULL};
 
     // create audio player
-    const SLInterfaceID ids[1] = {SL_IID_SEEK};
-    const SLboolean req[1] = {SL_BOOLEAN_TRUE};
+    const SLInterfaceID ids[3] = {SL_IID_SEEK, SL_IID_MUTESOLO, SL_IID_VOLUME};
+    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
     result = (*engineEngine)->CreateAudioPlayer(engineEngine, &uriPlayerObject, &audioSrc,
-            &audioSnk, 1, ids, req);
+            &audioSnk, 3, ids, req);
     // note that an invalid URI is not detected here, but during prepare/prefetch on Android,
     // or possibly during Realize on other platforms
     assert(SL_RESULT_SUCCESS == result);
@@ -283,8 +301,12 @@
     result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_SEEK, &uriPlayerSeek);
     assert(SL_RESULT_SUCCESS == result);
 
-    // enable whole file looping
-    result = (*uriPlayerSeek)->SetLoop(uriPlayerSeek, SL_BOOLEAN_TRUE, 0, SL_TIME_UNKNOWN);
+    // get the mute/solo interface
+    result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_MUTESOLO, &uriPlayerMuteSolo);
+    assert(SL_RESULT_SUCCESS == result);
+
+    // get the volume interface
+    result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_VOLUME, &uriPlayerVolume);
     assert(SL_RESULT_SUCCESS == result);
 
     return JNI_TRUE;
@@ -292,6 +314,7 @@
 
 
 // set the playing state for the URI audio player
+// to PLAYING (true) or PAUSED (false)
 void Java_com_example_nativeaudio_NativeAudio_setPlayingUriAudioPlayer(JNIEnv* env,
         jclass clazz, jboolean isPlaying)
 {
@@ -310,6 +333,134 @@
 }
 
 
+// set the whole file looping state for the URI audio player
+void Java_com_example_nativeaudio_NativeAudio_setLoopingUriAudioPlayer(JNIEnv* env,
+        jclass clazz, jboolean isLooping)
+{
+    SLresult result;
+
+    // make sure the URI audio player was created
+    if (NULL != uriPlayerSeek) {
+
+        // set the looping state
+        result = (*uriPlayerSeek)->SetLoop(uriPlayerSeek, (SLboolean) isLooping, 0,
+                SL_TIME_UNKNOWN);
+        assert(SL_RESULT_SUCCESS == result);
+
+    }
+
+}
+
+
+// expose the mute/solo APIs to Java for one of the 3 players
+
+static SLMuteSoloItf getMuteSolo()
+{
+    if (uriPlayerMuteSolo != NULL)
+        return uriPlayerMuteSolo;
+    else if (fdPlayerMuteSolo != NULL)
+        return fdPlayerMuteSolo;
+    else
+        return bqPlayerMuteSolo;
+}
+
+void Java_com_example_nativeaudio_NativeAudio_setChannelMuteUriAudioPlayer(JNIEnv* env,
+        jclass clazz, jint chan, jboolean mute)
+{
+    SLresult result;
+    SLMuteSoloItf muteSoloItf = getMuteSolo();
+    if (NULL != muteSoloItf) {
+        result = (*muteSoloItf)->SetChannelMute(muteSoloItf, chan, mute);
+        assert(SL_RESULT_SUCCESS == result);
+    }
+}
+
+void Java_com_example_nativeaudio_NativeAudio_setChannelSoloUriAudioPlayer(JNIEnv* env,
+        jclass clazz, jint chan, jboolean solo)
+{
+    SLresult result;
+    SLMuteSoloItf muteSoloItf = getMuteSolo();
+    if (NULL != muteSoloItf) {
+        result = (*muteSoloItf)->SetChannelSolo(muteSoloItf, chan, solo);
+        assert(SL_RESULT_SUCCESS == result);
+    }
+}
+
+int Java_com_example_nativeaudio_NativeAudio_getNumChannelsUriAudioPlayer(JNIEnv* env, jclass clazz)
+{
+    SLuint8 numChannels;
+    SLresult result;
+    SLMuteSoloItf muteSoloItf = getMuteSolo();
+    if (NULL != muteSoloItf) {
+        result = (*muteSoloItf)->GetNumChannels(muteSoloItf, &numChannels);
+        if (SL_RESULT_PRECONDITIONS_VIOLATED == result) {
+            // channel count is not yet known
+            numChannels = 0;
+        } else {
+            assert(SL_RESULT_SUCCESS == result);
+        }
+    } else {
+        numChannels = 0;
+    }
+    return numChannels;
+}
+
+// expose the volume APIs to Java for one of the 3 players
+
+static SLVolumeItf getVolume()
+{
+    if (uriPlayerVolume != NULL)
+        return uriPlayerVolume;
+    else if (fdPlayerVolume != NULL)
+        return fdPlayerVolume;
+    else
+        return bqPlayerVolume;
+}
+
+void Java_com_example_nativeaudio_NativeAudio_setVolumeUriAudioPlayer(JNIEnv* env, jclass clazz,
+        jint millibel)
+{
+    SLresult result;
+    SLVolumeItf volumeItf = getVolume();
+    if (NULL != volumeItf) {
+        result = (*volumeItf)->SetVolumeLevel(volumeItf, millibel);
+        assert(SL_RESULT_SUCCESS == result);
+    }
+}
+
+void Java_com_example_nativeaudio_NativeAudio_setMuteUriAudioPlayer(JNIEnv* env, jclass clazz,
+        jboolean mute)
+{
+    SLresult result;
+    SLVolumeItf volumeItf = getVolume();
+    if (NULL != volumeItf) {
+        result = (*volumeItf)->SetMute(volumeItf, mute);
+        assert(SL_RESULT_SUCCESS == result);
+    }
+}
+
+void Java_com_example_nativeaudio_NativeAudio_enableStereoPositionUriAudioPlayer(JNIEnv* env,
+        jclass clazz, jboolean enable)
+{
+    SLresult result;
+    SLVolumeItf volumeItf = getVolume();
+    if (NULL != volumeItf) {
+        result = (*volumeItf)->EnableStereoPosition(volumeItf, enable);
+        assert(SL_RESULT_SUCCESS == result);
+    }
+}
+
+void Java_com_example_nativeaudio_NativeAudio_setStereoPositionUriAudioPlayer(JNIEnv* env,
+        jclass clazz, jint permille)
+{
+    SLresult result;
+    SLVolumeItf volumeItf = getVolume();
+    if (NULL != volumeItf) {
+        result = (*volumeItf)->SetStereoPosition(volumeItf, permille);
+        assert(SL_RESULT_SUCCESS == result);
+    }
+}
+
 // enable reverb on the buffer queue player
 jboolean Java_com_example_nativeaudio_NativeAudio_enableReverb(JNIEnv* env, jclass clazz,
         jboolean enabled)
@@ -426,10 +577,10 @@
     SLDataSink audioSnk = {&loc_outmix, NULL};
 
     // create audio player
-    const SLInterfaceID ids[1] = {SL_IID_SEEK};
-    const SLboolean req[1] = {SL_BOOLEAN_TRUE};
+    const SLInterfaceID ids[3] = {SL_IID_SEEK, SL_IID_MUTESOLO, SL_IID_VOLUME};
+    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
     result = (*engineEngine)->CreateAudioPlayer(engineEngine, &fdPlayerObject, &audioSrc, &audioSnk,
-            1, ids, req);
+            3, ids, req);
     assert(SL_RESULT_SUCCESS == result);
 
     // realize the player
@@ -444,6 +595,14 @@
     result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_SEEK, &fdPlayerSeek);
     assert(SL_RESULT_SUCCESS == result);
 
+    // get the mute/solo interface
+    result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_MUTESOLO, &fdPlayerMuteSolo);
+    assert(SL_RESULT_SUCCESS == result);
+
+    // get the volume interface
+    result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_VOLUME, &fdPlayerVolume);
+    assert(SL_RESULT_SUCCESS == result);
+
     // enable whole file looping
     result = (*fdPlayerSeek)->SetLoop(fdPlayerSeek, SL_BOOLEAN_TRUE, 0, SL_TIME_UNKNOWN);
     assert(SL_RESULT_SUCCESS == result);
@@ -562,6 +721,8 @@
         bqPlayerPlay = NULL;
         bqPlayerBufferQueue = NULL;
         bqPlayerEffectSend = NULL;
+        bqPlayerMuteSolo = NULL;
+        bqPlayerVolume = NULL;
     }
 
     // destroy file descriptor audio player object, and invalidate all associated interfaces
@@ -570,6 +731,8 @@
         fdPlayerObject = NULL;
         fdPlayerPlay = NULL;
         fdPlayerSeek = NULL;
+        fdPlayerMuteSolo = NULL;
+        fdPlayerVolume = NULL;
     }
 
     // destroy URI audio player object, and invalidate all associated interfaces
@@ -578,6 +741,8 @@
         uriPlayerObject = NULL;
         uriPlayerPlay = NULL;
         uriPlayerSeek = NULL;
+        uriPlayerMuteSolo = NULL;
+        uriPlayerVolume = NULL;
     }
 
     // destroy audio recorder object, and invalidate all associated interfaces
diff --git a/ndk/platforms/android-9/samples/native-audio/res/layout/main.xml b/ndk/platforms/android-9/samples/native-audio/res/layout/main.xml
index b9fb95a..eca928a 100644
--- a/ndk/platforms/android-9/samples/native-audio/res/layout/main.xml
+++ b/ndk/platforms/android-9/samples/native-audio/res/layout/main.xml
@@ -26,6 +26,11 @@
     android:layout_height="wrap_content"
     android:text="@string/hello"
     />
+<LinearLayout
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    >
 <Button
     android:id="@+id/hello"
     android:text="Hello"
@@ -45,20 +50,123 @@
     android:layout_height="wrap_content"
     />
 <Button
+    android:id="@+id/embedded_soundtrack"
+    android:text="Embedded\nsoundtrack"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    />
+</LinearLayout>
+<LinearLayout
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    >
+<Button
     android:id="@+id/reverb"
     android:text="Reverb"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     />
 <Button
-    android:id="@+id/embedded_soundtrack"
-    android:text="Embedded soundtrack"
+    android:id="@+id/mute_uri"
+    android:text="Mute"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     />
 <Button
+    android:id="@+id/enable_stereo_position_uri"
+    android:text="Enable SP"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    />
+</LinearLayout>
+<LinearLayout
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    >
+<Spinner
+    android:id="@+id/uri_spinner"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="URI spinner"
+    />
+</LinearLayout>
+<LinearLayout
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    >
+<Button
     android:id="@+id/uri_soundtrack"
-    android:text="URI soundtrack"
+    android:text="URI\nsoundtrack"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    />
+<Button
+    android:id="@+id/pause_uri"
+    android:text="Pause\nURI"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    />
+<Button
+    android:id="@+id/play_uri"
+    android:text="Play\nURI"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    />
+<Button
+    android:id="@+id/loop_uri"
+    android:text="Loop\nURI"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    />
+</LinearLayout>
+<LinearLayout
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    >
+    <Button
+        android:id="@+id/mute_left_uri"
+        android:text="mute left"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        />
+    <Button
+        android:id="@+id/mute_right_uri"
+        android:text="mute right"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        />
+    <Button
+        android:id="@+id/solo_left_uri"
+        android:text="solo left"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        />
+    <Button
+        android:id="@+id/solo_right_uri"
+        android:text="solo right"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        />
+</LinearLayout>
+<Button
+    android:id="@+id/channels_uri"
+    android:text="Get channels"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    />
+<SeekBar
+    android:id="@+id/volume_uri"
+    android:text="Volume"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    />
+<SeekBar
+    android:id="@+id/pan_uri"
+    android:text="Pan"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     />
diff --git a/ndk/platforms/android-9/samples/native-audio/res/values/strings.xml b/ndk/platforms/android-9/samples/native-audio/res/values/strings.xml
index 280c103..f6f2834 100644
--- a/ndk/platforms/android-9/samples/native-audio/res/values/strings.xml
+++ b/ndk/platforms/android-9/samples/native-audio/res/values/strings.xml
@@ -19,4 +19,10 @@
 <resources>
     <string name="hello">Hello, Android using native audio!</string>
     <string name="app_name">NativeAudio</string>
+
+    <string-array name="uri_spinner_array">
+        <item>http://upload.wikimedia.org/wikipedia/commons/6/6d/Banana.ogg</item>
+        <item>http://www.freesound.org/data/previews/18/18765_18799-lq.mp3</item>
+    </string-array>
+
 </resources>
diff --git a/ndk/platforms/android-9/samples/native-audio/src/com/example/nativeaudio/NativeAudio.java b/ndk/platforms/android-9/samples/native-audio/src/com/example/nativeaudio/NativeAudio.java
index 5a7b3ef..7138123 100644
--- a/ndk/platforms/android-9/samples/native-audio/src/com/example/nativeaudio/NativeAudio.java
+++ b/ndk/platforms/android-9/samples/native-audio/src/com/example/nativeaudio/NativeAudio.java
@@ -19,24 +19,35 @@
 import android.app.Activity;
 import android.content.res.AssetManager;
 import android.os.Bundle;
+//import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.Spinner;
+import android.widget.Toast;
 
 public class NativeAudio extends Activity {
 
+    //static final String TAG = "NativeAudio";
+
     static final int CLIP_NONE = 0;
     static final int CLIP_HELLO = 1;
     static final int CLIP_ANDROID = 2;
     static final int CLIP_SAWTOOTH = 3;
     static final int CLIP_PLAYBACK = 4;
 
-    static final String URI = "http://upload.wikimedia.org/wikipedia/commons/6/6d/Banana.ogg";
+    static String URI;
     static AssetManager assetManager;
 
     static boolean isPlayingAsset = false;
     static boolean isPlayingUri = false;
 
+    static int numChannelsUri = 0;
+
     /** Called when the activity is first created. */
     @Override
     protected void onCreate(Bundle icicle) {
@@ -50,6 +61,24 @@
         createEngine();
         createBufferQueueAudioPlayer();
 
+        // initialize URI spinner
+        Spinner uriSpinner = (Spinner) findViewById(R.id.uri_spinner);
+        ArrayAdapter<CharSequence> uriAdapter = ArrayAdapter.createFromResource(
+                this, R.array.uri_spinner_array, android.R.layout.simple_spinner_item);
+        uriAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        uriSpinner.setAdapter(uriAdapter);
+        uriSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+
+            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                URI = parent.getItemAtPosition(pos).toString();
+            }
+
+            public void onNothingSelected(AdapterView parent) {
+                URI = null;
+            }
+
+        });
+
         // initialize button click handlers
 
         ((Button) findViewById(R.id.hello)).setOnClickListener(new OnClickListener() {
@@ -99,16 +128,122 @@
         ((Button) findViewById(R.id.uri_soundtrack)).setOnClickListener(new OnClickListener() {
             boolean created = false;
             public void onClick(View view) {
-                if (!created) {
+                if (!created && URI != null) {
                     created = createUriAudioPlayer(URI);
                 }
-                if (created) {
-                    isPlayingUri = !isPlayingUri;
-                    setPlayingUriAudioPlayer(isPlayingUri);
-                }
              }
         });
 
+        ((Button) findViewById(R.id.pause_uri)).setOnClickListener(new OnClickListener() {
+            public void onClick(View view) {
+                setPlayingUriAudioPlayer(false);
+             }
+        });
+
+        ((Button) findViewById(R.id.play_uri)).setOnClickListener(new OnClickListener() {
+            public void onClick(View view) {
+                setPlayingUriAudioPlayer(true);
+             }
+        });
+
+        ((Button) findViewById(R.id.loop_uri)).setOnClickListener(new OnClickListener() {
+            boolean isLooping = false;
+            public void onClick(View view) {
+                isLooping = !isLooping;
+                setLoopingUriAudioPlayer(isLooping);
+             }
+        });
+
+        ((Button) findViewById(R.id.mute_left_uri)).setOnClickListener(new OnClickListener() {
+            boolean muted = false;
+            public void onClick(View view) {
+                muted = !muted;
+                setChannelMuteUriAudioPlayer(0, muted);
+             }
+        });
+
+        ((Button) findViewById(R.id.mute_right_uri)).setOnClickListener(new OnClickListener() {
+            boolean muted = false;
+            public void onClick(View view) {
+                muted = !muted;
+                setChannelMuteUriAudioPlayer(1, muted);
+             }
+        });
+
+        ((Button) findViewById(R.id.solo_left_uri)).setOnClickListener(new OnClickListener() {
+            boolean soloed = false;
+            public void onClick(View view) {
+                soloed = !soloed;
+                setChannelSoloUriAudioPlayer(0, soloed);
+             }
+        });
+
+        ((Button) findViewById(R.id.solo_right_uri)).setOnClickListener(new OnClickListener() {
+            boolean soloed = false;
+            public void onClick(View view) {
+                soloed = !soloed;
+                setChannelSoloUriAudioPlayer(1, soloed);
+             }
+        });
+
+        ((Button) findViewById(R.id.mute_uri)).setOnClickListener(new OnClickListener() {
+            boolean muted = false;
+            public void onClick(View view) {
+                muted = !muted;
+                setMuteUriAudioPlayer(muted);
+             }
+        });
+
+        ((Button) findViewById(R.id.enable_stereo_position_uri)).setOnClickListener(
+                new OnClickListener() {
+            boolean enabled = false;
+            public void onClick(View view) {
+                enabled = !enabled;
+                enableStereoPositionUriAudioPlayer(enabled);
+             }
+        });
+
+        ((Button) findViewById(R.id.channels_uri)).setOnClickListener(new OnClickListener() {
+            public void onClick(View view) {
+                if (numChannelsUri == 0) {
+                    numChannelsUri = getNumChannelsUriAudioPlayer();
+                }
+                Toast.makeText(NativeAudio.this, "Channels: " + numChannelsUri,
+                        Toast.LENGTH_SHORT).show();
+             }
+        });
+
+        ((SeekBar) findViewById(R.id.volume_uri)).setOnSeekBarChangeListener(
+                new OnSeekBarChangeListener() {
+            int lastProgress = 100;
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                assert progress >= 0 && progress <= 100;
+                lastProgress = progress;
+            }
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                int attenuation = 100 - lastProgress;
+                int millibel = attenuation * -50;
+                setVolumeUriAudioPlayer(millibel);
+            }
+        });
+
+        ((SeekBar) findViewById(R.id.pan_uri)).setOnSeekBarChangeListener(
+                new OnSeekBarChangeListener() {
+            int lastProgress = 100;
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                assert progress >= 0 && progress <= 100;
+                lastProgress = progress;
+            }
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                int permille = (lastProgress - 50) * 20;
+                setStereoPositionUriAudioPlayer(permille);
+            }
+        });
+
         ((Button) findViewById(R.id.record)).setOnClickListener(new OnClickListener() {
             boolean created = false;
             public void onClick(View view) {
@@ -155,9 +290,18 @@
     public static native void createEngine();
     public static native void createBufferQueueAudioPlayer();
     public static native boolean createAssetAudioPlayer(AssetManager assetManager, String filename);
+    // true == PLAYING, false == PAUSED
     public static native void setPlayingAssetAudioPlayer(boolean isPlaying);
     public static native boolean createUriAudioPlayer(String uri);
     public static native void setPlayingUriAudioPlayer(boolean isPlaying);
+    public static native void setLoopingUriAudioPlayer(boolean isLooping);
+    public static native void setChannelMuteUriAudioPlayer(int chan, boolean mute);
+    public static native void setChannelSoloUriAudioPlayer(int chan, boolean solo);
+    public static native int getNumChannelsUriAudioPlayer();
+    public static native void setVolumeUriAudioPlayer(int millibel);
+    public static native void setMuteUriAudioPlayer(boolean mute);
+    public static native void enableStereoPositionUriAudioPlayer(boolean enable);
+    public static native void setStereoPositionUriAudioPlayer(int permille);
     public static native boolean selectClip(int which, int count);
     public static native boolean enableReverb(boolean enabled);
     public static native boolean createAudioRecorder();