text: /*
   * Copyright (C) 2010 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   */
  
  /* This is a JNI example where we use native methods to play sounds
   * using OpenSL ES. See the corresponding Java source file located at:
   *
   *   src/com/example/nativeaudio/NativeAudio/NativeAudio.java
   */
  
  #include <assert.h>
  #include <jni.h>
  #include <string.h>
  #include <pthread.h>
  
  
  // for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");
  // #include <android/log.h>
  
  // for native audio
  #include <SLES/OpenSLES.h>
  #include <SLES/OpenSLES_Android.h>
  
  // for native asset manager
  #include <sys/types.h>
  #include <android/asset_manager.h>
  #include <android/asset_manager_jni.h>
  
  // pre-recorded sound clips, both are 8 kHz mono 16-bit signed little endian
  static const char hello[] =
  #include "hello_clip.h"
  ;
  
  static const char android[] =
  #include "android_clip.h"
  ;
  
  // engine interfaces
  static SLObjectItf engineObject = NULL;
  static SLEngineItf engineEngine;
  
  // output mix interfaces
  static SLObjectItf outputMixObject = NULL;
  static SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
  
  // buffer queue player interfaces
  static SLObjectItf bqPlayerObject = NULL;
  static SLPlayItf bqPlayerPlay;
  static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
  static SLEffectSendItf bqPlayerEffectSend;
  static SLMuteSoloItf bqPlayerMuteSolo;
  static SLVolumeItf bqPlayerVolume;
  static SLmilliHertz bqPlayerSampleRate = 0;
  static jint   bqPlayerBufSize = 0;
  static short *resampleBuf = NULL;
  // a mutext to guard against re-entrance to record & playback
  // as well as make recording and playing back to be mutually exclusive
  // this is to avoid crash at situations like:
  //    recording is in session [not finished]
  //    user presses record button and another recording coming in
  // The action: when recording/playing back is not finished, ignore the new request
  static pthread_mutex_t  audioEngineLock = PTHREAD_MUTEX_INITIALIZER;
  
  // aux effect on the output mix, used by the buffer queue player
  static const SLEnvironmentalReverbSettings reverbSettings =
      SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;
  
  // URI player interfaces
  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;
  static SLRecordItf recorderRecord;
  static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
  
  // synthesized sawtooth clip
  #define SAWTOOTH_FRAMES 8000
  static short sawtoothBuffer[SAWTOOTH_FRAMES];
  
  // 5 seconds of recorded audio at 16 kHz mono, 16-bit signed little endian
  #define RECORDER_FRAMES (16000 * 5)
  static short recorderBuffer[RECORDER_FRAMES];
  static unsigned recorderSize = 0;
  
  // pointer and size of the next player buffer to enqueue, and number of remaining buffers
  static short *nextBuffer;
  static unsigned nextSize;
  static int nextCount;
  
  
  // synthesize a mono sawtooth wave and place it into a buffer (called automatically on load)
  __attribute__((constructor)) static void onDlOpen(void)
  {
      unsigned i;
      for (i = 0; i < SAWTOOTH_FRAMES; ++i) {
          sawtoothBuffer[i] = 32768 - ((i % 100) * 660);
      }
  }
  
  void releaseResampleBuf(void) {
      if( 0 == bqPlayerSampleRate) {
          /*
           * we are not using fast path, so we were not creating buffers, nothing to do
           */
          return;
      }
  
      free(resampleBuf);
      resampleBuf = NULL;
  }
  
  /*
   * Only support up-sampling
   */
  short* createResampledBuf(uint32_t idx, uint32_t srcRate, unsigned *size) {
      short  *src = NULL;
      short  *workBuf;
      int    upSampleRate;
      int32_t srcSampleCount = 0;
  
      if(0 == bqPlayerSampleRate) {
          return NULL;
      }
      if(bqPlayerSampleRate % srcRate) {
          /*
           * simple up-sampling, must be divisible
           */
          return NULL;
      }
      upSampleRate = bqPlayerSampleRate / srcRate;
  
      switch (idx) {
          case 0:
              return NULL;
          case 1: // HELLO_CLIP
              srcSampleCount = sizeof(hello) >> 1;
              src = (short*)hello;
              break;
          case 2: // ANDROID_CLIP
              srcSampleCount = sizeof(android) >> 1;
              src = (short*) android;
              break;
          case 3: // SAWTOOTH_CLIP
              srcSampleCount = SAWTOOTH_FRAMES;
              src = sawtoothBuffer;
              break;
          case 4: // captured frames
              srcSampleCount = recorderSize / sizeof(short);
              src =  recorderBuffer;
              break;
          default:
              assert(0);
              return NULL;
      }
  
      resampleBuf = (short*) malloc((srcSampleCount * upSampleRate) << 1);
      if(resampleBuf == NULL) {
          return resampleBuf;
      }
      workBuf = resampleBuf;
      for(int sample=0; sample < srcSampleCount; sample++) {
          for(int dup = 0; dup  < upSampleRate; dup++) {
              *workBuf++ = src[sample];
          }
      }
  
      *size = (srcSampleCount * upSampleRate) << 1;     // sample format is 16 bit
      return resampleBuf;
  }
  
  // this callback handler is called every time a buffer finishes playing
  void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
  {
      assert(bq == bqPlayerBufferQueue);
      assert(NULL == context);
      // for streaming playback, replace this test by logic to find and fill the next buffer
      if (--nextCount > 0 && NULL != nextBuffer && 0 != nextSize) {
          SLresult result;
          // enqueue another buffer
          result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
          // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
          // which for this code example would indicate a programming error
          if (SL_RESULT_SUCCESS != result) {
              pthread_mutex_unlock(&audioEngineLock);
          }
          (void)result;
      } else {
          releaseResampleBuf();
          pthread_mutex_unlock(&audioEngineLock);
      }
  }
  
  
  // this callback handler is called every time a buffer finishes recording
  void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
  {
      assert(bq == recorderBufferQueue);
      assert(NULL == context);
      // for streaming recording, here we would call Enqueue to give recorder the next buffer to fill
      // but instead, this is a one-time buffer so we stop recording
      SLresult result;
      result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
      if (SL_RESULT_SUCCESS == result) {
          recorderSize = RECORDER_FRAMES * sizeof(short);
      }
      pthread_mutex_unlock(&audioEngineLock);
  }
  
  
  // create the engine and output mix objects
  void Java_com_example_nativeaudio_NativeAudio_createEngine(JNIEnv* env, jclass clazz)
  {
      SLresult result;
  
      // create engine
      result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // realize the engine
      result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the engine interface, which is needed in order to create other objects
      result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // create output mix, with environmental reverb specified as a non-required interface
      const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};
      const SLboolean req[1] = {SL_BOOLEAN_FALSE};
      result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // realize the output mix
      result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the environmental reverb interface
      // this could fail if the environmental reverb effect is not available,
      // either because the feature is not present, excessive CPU load, or
      // the required MODIFY_AUDIO_SETTINGS permission was not requested and granted
      result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
              &outputMixEnvironmentalReverb);
      if (SL_RESULT_SUCCESS == result) {
          result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                  outputMixEnvironmentalReverb, &reverbSettings);
          (void)result;
      }
      // ignore unsuccessful result codes for environmental reverb, as it is optional for this example
  
  }
  
  
  // create buffer queue audio player
  void Java_com_example_nativeaudio_NativeAudio_createBufferQueueAudioPlayer(JNIEnv* env,
          jclass clazz, jint sampleRate, jint bufSize)
  {
      SLresult result;
      if (sampleRate >= 0 && bufSize >= 0 ) {
          bqPlayerSampleRate = sampleRate * 1000;
          /*
           * device native buffer size is another factor to minimize audio latency, not used in this
           * sample: we only play one giant buffer here
           */
          bqPlayerBufSize = bufSize;
      }
  
      // configure audio source
      SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
      SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_8,
          SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
          SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
      /*
       * Enable Fast Audio when possible:  once we set the same rate to be the native, fast audio path
       * will be triggered
       */
      if(bqPlayerSampleRate) {
          format_pcm.samplesPerSec = bqPlayerSampleRate;       //sample rate in mili second
      }
      SLDataSource audioSrc = {&loc_bufq, &format_pcm};
  
      // configure audio sink
      SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
      SLDataSink audioSnk = {&loc_outmix, NULL};
  
      /*
       * create audio player:
       *     fast audio does not support when SL_IID_EFFECTSEND is required, skip it
       *     for fast audio case
       */
      const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_EFFECTSEND,
                                      /*SL_IID_MUTESOLO,*/};
      const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
                                     /*SL_BOOLEAN_TRUE,*/ };
  
      result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
              bqPlayerSampleRate? 2 : 3, ids, req);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // realize the player
      result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the play interface
      result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the buffer queue interface
      result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
              &bqPlayerBufferQueue);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // register callback on the buffer queue
      result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the effect send interface
      bqPlayerEffectSend = NULL;
      if( 0 == bqPlayerSampleRate) {
          result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND,
                                                   &bqPlayerEffectSend);
          assert(SL_RESULT_SUCCESS == result);
          (void)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);
      (void)result;
  #endif
  
      // get the volume interface
      result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // set the player's state to playing
      result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  }
  
  
  // create URI audio player
  jboolean Java_com_example_nativeaudio_NativeAudio_createUriAudioPlayer(JNIEnv* env, jclass clazz,
          jstring uri)
  {
      SLresult result;
  
      // convert Java string to UTF-8
      const char *utf8 = (*env)->GetStringUTFChars(env, uri, NULL);
      assert(NULL != utf8);
  
      // configure audio source
      // (requires the INTERNET permission depending on the uri parameter)
      SLDataLocator_URI loc_uri = {SL_DATALOCATOR_URI, (SLchar *) utf8};
      SLDataFormat_MIME format_mime = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
      SLDataSource audioSrc = {&loc_uri, &format_mime};
  
      // configure audio sink
      SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
      SLDataSink audioSnk = {&loc_outmix, NULL};
  
      // create audio player
      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, 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);
      (void)result;
  
      // release the Java string and UTF-8
      (*env)->ReleaseStringUTFChars(env, uri, utf8);
  
      // realize the player
      result = (*uriPlayerObject)->Realize(uriPlayerObject, SL_BOOLEAN_FALSE);
      // this will always succeed on Android, but we check result for portability to other platforms
      if (SL_RESULT_SUCCESS != result) {
          (*uriPlayerObject)->Destroy(uriPlayerObject);
          uriPlayerObject = NULL;
          return JNI_FALSE;
      }
  
      // get the play interface
      result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_PLAY, &uriPlayerPlay);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the seek interface
      result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_SEEK, &uriPlayerSeek);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the mute/solo interface
      result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_MUTESOLO, &uriPlayerMuteSolo);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the volume interface
      result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_VOLUME, &uriPlayerVolume);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      return JNI_TRUE;
  }
  
  
  // 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)
  {
      SLresult result;
  
      // make sure the URI audio player was created
      if (NULL != uriPlayerPlay) {
  
          // set the player's state
          result = (*uriPlayerPlay)->SetPlayState(uriPlayerPlay, isPlaying ?
              SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED);
          assert(SL_RESULT_SUCCESS == result);
          (void)result;
      }
  
  }
  
  
  // 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);
          (void)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)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);
          (void)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)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)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)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);
          (void)result;
      }
  }
  
  // enable reverb on the buffer queue player
  jboolean Java_com_example_nativeaudio_NativeAudio_enableReverb(JNIEnv* env, jclass clazz,
          jboolean enabled)
  {
      SLresult result;
  
      // we might not have been able to add environmental reverb to the output mix
      if (NULL == outputMixEnvironmentalReverb) {
          return JNI_FALSE;
      }
  
      if(bqPlayerSampleRate) {
          /*
           * we are in fast audio, reverb is not supported.
           */
          return JNI_FALSE;
      }
      result = (*bqPlayerEffectSend)->EnableEffectSend(bqPlayerEffectSend,
              outputMixEnvironmentalReverb, (SLboolean) enabled, (SLmillibel) 0);
      // and even if environmental reverb was present, it might no longer be available
      if (SL_RESULT_SUCCESS != result) {
          return JNI_FALSE;
      }
  
      return JNI_TRUE;
  }
  
  // select the desired clip and play count, and enqueue the first buffer if idle
  jboolean Java_com_example_nativeaudio_NativeAudio_selectClip(JNIEnv* env, jclass clazz, jint which,
          jint count)
  {
      if (pthread_mutex_trylock(&audioEngineLock)) {
          // If we could not acquire audio engine lock, reject this request and client should re-try
          return JNI_FALSE;
      }
      switch (which) {
      case 0:     // CLIP_NONE
          nextBuffer = (short *) NULL;
          nextSize = 0;
          break;
      case 1:     // CLIP_HELLO
          nextBuffer = createResampledBuf(1, SL_SAMPLINGRATE_8, &nextSize);
          if(!nextBuffer) {
              nextBuffer = (short*)hello;
              nextSize  = sizeof(hello);
          }
          break;
      case 2:     // CLIP_ANDROID
          nextBuffer = createResampledBuf(2, SL_SAMPLINGRATE_8, &nextSize);
          if(!nextBuffer) {
              nextBuffer = (short*)android;
              nextSize  = sizeof(android);
          }
          break;
      case 3:     // CLIP_SAWTOOTH
          nextBuffer = createResampledBuf(3, SL_SAMPLINGRATE_8, &nextSize);
          if(!nextBuffer) {
              nextBuffer = (short*)sawtoothBuffer;
              nextSize  = sizeof(sawtoothBuffer);
          }
          break;
      case 4:     // CLIP_PLAYBACK
          nextBuffer = createResampledBuf(4, SL_SAMPLINGRATE_16, &nextSize);
          // we recorded at 16 kHz, but are playing buffers at 8 Khz, so do a primitive down-sample
          if(!nextBuffer) {
              unsigned i;
              for (i = 0; i < recorderSize; i += 2 * sizeof(short)) {
                  recorderBuffer[i >> 2] = recorderBuffer[i >> 1];
              }
              recorderSize >>= 1;
              nextBuffer = recorderBuffer;
              nextSize = recorderSize;
          }
          break;
      default:
          nextBuffer = NULL;
          nextSize = 0;
          break;
      }
      nextCount = count;
      if (nextSize > 0) {
          // here we only enqueue one buffer because it is a long clip,
          // but for streaming playback we would typically enqueue at least 2 buffers to start
          SLresult result;
          result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
          if (SL_RESULT_SUCCESS != result) {
              pthread_mutex_unlock(&audioEngineLock);
              return JNI_FALSE;
          }
      }
  
      return JNI_TRUE;
  }
  
  
  // create asset audio player
  jboolean Java_com_example_nativeaudio_NativeAudio_createAssetAudioPlayer(JNIEnv* env, jclass clazz,
          jobject assetManager, jstring filename)
  {
      SLresult result;
  
      // convert Java string to UTF-8
      const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);
      assert(NULL != utf8);
  
      // use asset manager to open asset by filename
      AAssetManager* mgr = AAssetManager_fromJava(env, assetManager);
      assert(NULL != mgr);
      AAsset* asset = AAssetManager_open(mgr, utf8, AASSET_MODE_UNKNOWN);
  
      // release the Java string and UTF-8
      (*env)->ReleaseStringUTFChars(env, filename, utf8);
  
      // the asset might not be found
      if (NULL == asset) {
          return JNI_FALSE;
      }
  
      // open asset as file descriptor
      off_t start, length;
      int fd = AAsset_openFileDescriptor(asset, &start, &length);
      assert(0 <= fd);
      AAsset_close(asset);
  
      // configure audio source
      SLDataLocator_AndroidFD loc_fd = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};
      SLDataFormat_MIME format_mime = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
      SLDataSource audioSrc = {&loc_fd, &format_mime};
  
      // configure audio sink
      SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
      SLDataSink audioSnk = {&loc_outmix, NULL};
  
      // create audio player
      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,
              3, ids, req);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // realize the player
      result = (*fdPlayerObject)->Realize(fdPlayerObject, SL_BOOLEAN_FALSE);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the play interface
      result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_PLAY, &fdPlayerPlay);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the seek interface
      result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_SEEK, &fdPlayerSeek);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the mute/solo interface
      result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_MUTESOLO, &fdPlayerMuteSolo);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the volume interface
      result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_VOLUME, &fdPlayerVolume);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // enable whole file looping
      result = (*fdPlayerSeek)->SetLoop(fdPlayerSeek, SL_BOOLEAN_TRUE, 0, SL_TIME_UNKNOWN);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      return JNI_TRUE;
  }
  
  // set the playing state for the asset audio player
  void Java_com_example_nativeaudio_NativeAudio_setPlayingAssetAudioPlayer(JNIEnv* env,
          jclass clazz, jboolean isPlaying)
  {
      SLresult result;
  
      // make sure the asset audio player was created
      if (NULL != fdPlayerPlay) {
  
          // set the player's state
          result = (*fdPlayerPlay)->SetPlayState(fdPlayerPlay, isPlaying ?
              SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED);
          assert(SL_RESULT_SUCCESS == result);
          (void)result;
      }
  
  }
  
  // create audio recorder: recorder is not in fast path
  //    like to avoid excessive re-sampling while playing back from Hello & Android clip
  jboolean Java_com_example_nativeaudio_NativeAudio_createAudioRecorder(JNIEnv* env, jclass clazz)
  {
      SLresult result;
  
      // configure audio source
      SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
              SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
      SLDataSource audioSrc = {&loc_dev, NULL};
  
      // configure audio sink
      SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
      SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_16,
          SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
          SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
      SLDataSink audioSnk = {&loc_bq, &format_pcm};
  
      // create audio recorder
      // (requires the RECORD_AUDIO permission)
      const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
      const SLboolean req[1] = {SL_BOOLEAN_TRUE};
      result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc,
              &audioSnk, 1, id, req);
      if (SL_RESULT_SUCCESS != result) {
          return JNI_FALSE;
      }
  
      // realize the audio recorder
      result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
      if (SL_RESULT_SUCCESS != result) {
          return JNI_FALSE;
      }
  
      // get the record interface
      result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // get the buffer queue interface
      result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
              &recorderBufferQueue);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // register callback on the buffer queue
      result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback,
              NULL);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      return JNI_TRUE;
  }
  
  
  // set the recording state for the audio recorder
  void Java_com_example_nativeaudio_NativeAudio_startRecording(JNIEnv* env, jclass clazz)
  {
      SLresult result;
  
      if (pthread_mutex_trylock(&audioEngineLock)) {
          return;
      }
      // in case already recording, stop recording and clear buffer queue
      result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
      result = (*recorderBufferQueue)->Clear(recorderBufferQueue);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // the buffer is not valid for playback yet
      recorderSize = 0;
  
      // enqueue an empty buffer to be filled by the recorder
      // (for streaming recording, we would enqueue at least 2 empty buffers to start things off)
      result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recorderBuffer,
              RECORDER_FRAMES * sizeof(short));
      // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
      // which for this code example would indicate a programming error
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  
      // start recording
      result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
      assert(SL_RESULT_SUCCESS == result);
      (void)result;
  }
  
  
  // shut down the native audio system
  void Java_com_example_nativeaudio_NativeAudio_shutdown(JNIEnv* env, jclass clazz)
  {
  
      // destroy buffer queue audio player object, and invalidate all associated interfaces
      if (bqPlayerObject != NULL) {
          (*bqPlayerObject)->Destroy(bqPlayerObject);
          bqPlayerObject = NULL;
          bqPlayerPlay = NULL;
          bqPlayerBufferQueue = NULL;
          bqPlayerEffectSend = NULL;
          bqPlayerMuteSolo = NULL;
          bqPlayerVolume = NULL;
      }
  
      // destroy file descriptor audio player object, and invalidate all associated interfaces
      if (fdPlayerObject != NULL) {
          (*fdPlayerObject)->Destroy(fdPlayerObject);
          fdPlayerObject = NULL;
          fdPlayerPlay = NULL;
          fdPlayerSeek = NULL;
          fdPlayerMuteSolo = NULL;
          fdPlayerVolume = NULL;
      }
  
      // destroy URI audio player object, and invalidate all associated interfaces
      if (uriPlayerObject != NULL) {
          (*uriPlayerObject)->Destroy(uriPlayerObject);
          uriPlayerObject = NULL;
          uriPlayerPlay = NULL;
          uriPlayerSeek = NULL;
          uriPlayerMuteSolo = NULL;
          uriPlayerVolume = NULL;
      }
  
      // destroy audio recorder object, and invalidate all associated interfaces
      if (recorderObject != NULL) {
          (*recorderObject)->Destroy(recorderObject);
          recorderObject = NULL;
          recorderRecord = NULL;
          recorderBufferQueue = NULL;
      }
  
      // destroy output mix object, and invalidate all associated interfaces
      if (outputMixObject != NULL) {
          (*outputMixObject)->Destroy(outputMixObject);
          outputMixObject = NULL;
          outputMixEnvironmentalReverb = NULL;
      }
  
      // destroy engine object, and invalidate all associated interfaces
      if (engineObject != NULL) {
          (*engineObject)->Destroy(engineObject);
          engineObject = NULL;
          engineEngine = NULL;
      }
  
      pthread_mutex_destroy(&audioEngineLock);
  }
  