Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <android/log.h>
     18 #include <assert.h>
     19 #include <jni.h>
     20 #include <malloc.h>
     21 #include <math.h>
     22 #include <sys/types.h>
     23 
     24 // for native audio
     25 #include <SLES/OpenSLES.h>
     26 #include <SLES/OpenSLES_Android.h>
     27 #include <SLES/OpenSLES_AndroidConfiguration.h>
     28 
     29 #include "sync_clock.h"
     30 
     31 // logging
     32 #define APPNAME "WALT"
     33 
     34 // engine interfaces
     35 static SLObjectItf engineObject = NULL;
     36 static SLEngineItf engineEngine = NULL;
     37 
     38 // output mix interfaces
     39 static SLObjectItf outputMixObject = NULL;
     40 
     41 // buffer queue player interfaces
     42 static SLObjectItf bqPlayerObject = NULL;
     43 static SLPlayItf bqPlayerPlay = NULL;
     44 static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
     45 
     46 // recorder interfaces
     47 static SLObjectItf recorderObject = NULL;
     48 static SLRecordItf recorderRecord;
     49 static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
     50 static volatile int bqPlayerRecorderBusy = 0;
     51 
     52 static unsigned int recorder_frames;
     53 static short* recorderBuffer;
     54 static unsigned recorderSize = 0;
     55 
     56 static unsigned int framesPerBuffer;
     57 
     58 #define CHANNELS 1  // 1 for mono, 2 for stereo
     59 
     60 // Each short represents a 16-bit audio sample
     61 static short* beepBuffer = NULL;
     62 static short* silenceBuffer = NULL;
     63 static unsigned int bufferSizeInBytes = 0;
     64 
     65 #define MAXIMUM_AMPLITUDE_VALUE 32767
     66 
     67 // how many times to play the wave table (so we can actually hear it)
     68 #define BUFFERS_TO_PLAY 10
     69 
     70 static unsigned buffersRemaining = 0;
     71 static short warmedUp = 0;
     72 
     73 
     74 // Timestamps
     75 // te - enqueue time
     76 // tc - callback time
     77 int64_t te_play = 0, te_rec = 0, tc_rec = 0;
     78 
     79 /**
     80  * Create wave tables for audio out.
     81  */
     82 void createWaveTables(){
     83     bufferSizeInBytes = framesPerBuffer * sizeof(*beepBuffer);
     84     silenceBuffer = malloc(bufferSizeInBytes);
     85     beepBuffer = malloc(bufferSizeInBytes);
     86 
     87 
     88     __android_log_print(ANDROID_LOG_VERBOSE,
     89                         APPNAME,
     90                         "Creating wave tables, 1 channel. Frames: %i Buffer size (bytes): %i",
     91                         framesPerBuffer,
     92                         bufferSizeInBytes);
     93 
     94     unsigned int i;
     95     for (i = 0; i < framesPerBuffer; i++) {
     96         silenceBuffer[i] = 0;
     97         beepBuffer[i] = (i & 2 - 1) * MAXIMUM_AMPLITUDE_VALUE;
     98         // This fills a buffer that looks like [min, min, max, max, min, min...]
     99         // which is a square wave at 1/4 frequency of the sampling rate
    100         // for 48kHz sampling this is 12kHz pitch, still well audible.
    101     }
    102 }
    103 
    104 // this callback handler is called every time a buffer finishes playing
    105 void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, __attribute__((unused)) void *context)
    106 {
    107     if (bq == NULL) {
    108         __android_log_print(ANDROID_LOG_ERROR, APPNAME, "buffer queue is null");
    109     }
    110     assert(bq == bqPlayerBufferQueue);
    111     assert(NULL == context);
    112 
    113     if (buffersRemaining > 0) { // continue playing tone
    114         if(buffersRemaining == BUFFERS_TO_PLAY && warmedUp) {
    115             // Enqueue the first non-silent buffer, save the timestamp
    116             // For cold test Enqueue happens in playTone rather than here.
    117             te_play = uptimeMicros();
    118         }
    119         buffersRemaining--;
    120 
    121         SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, beepBuffer,
    122                                                           bufferSizeInBytes);
    123         (void)result;
    124         assert(SL_RESULT_SUCCESS == result);
    125     } else if (warmedUp) {      // stop tone but keep playing silence
    126         SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, silenceBuffer,
    127                                                  bufferSizeInBytes);
    128         assert(SL_RESULT_SUCCESS == result);
    129         (void) result;
    130     } else {                    // stop playing completely
    131         SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
    132         assert(SL_RESULT_SUCCESS == result);
    133         (void)result;
    134 
    135         __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Done playing tone");
    136     }
    137 }
    138 
    139 jlong Java_org_chromium_latency_walt_AudioTest_playTone(__attribute__((unused)) JNIEnv* env,
    140                                                         __attribute__((unused)) jclass clazz){
    141 
    142     int64_t t_start = uptimeMicros();
    143     te_play = 0;
    144 
    145     SLresult result;
    146 
    147     if (!warmedUp) {
    148         result = (*bqPlayerBufferQueue)->Clear(bqPlayerBufferQueue);
    149         assert(SL_RESULT_SUCCESS == result);
    150         (void)result;
    151 
    152         // Enqueue first buffer
    153         te_play = uptimeMicros();
    154         result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, beepBuffer,
    155                                                  bufferSizeInBytes);
    156         assert(SL_RESULT_SUCCESS == result);
    157         (void) result;
    158 
    159         result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
    160         assert(SL_RESULT_SUCCESS == result);
    161         (void) result;
    162 
    163         int dt_state = uptimeMicros() - t_start;
    164         __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "playTone() changed state to playing dt=%d us", dt_state);
    165         // TODO: this block takes lots of time (~13ms on Nexus 7) research this and decide how to measure.
    166     }
    167 
    168     __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Playing tone");
    169     buffersRemaining = BUFFERS_TO_PLAY;
    170 
    171     return (jlong) t_start;
    172 }
    173 
    174 
    175 // create the engine and output mix objects
    176 void Java_org_chromium_latency_walt_AudioTest_createEngine(__attribute__((unused)) JNIEnv* env,
    177                                                            __attribute__((unused)) jclass clazz)
    178 {
    179     __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Creating audio engine");
    180 
    181     SLresult result;
    182 
    183     // create engine
    184     result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    185     assert(SL_RESULT_SUCCESS == result);
    186     (void)result;
    187 
    188     // realize the engine
    189     result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    190     assert(SL_RESULT_SUCCESS == result);
    191     (void)result;
    192 
    193     // get the engine interface, which is needed in order to create other objects
    194     result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    195     assert(SL_RESULT_SUCCESS == result);
    196     (void)result;
    197 
    198     // create output mix,
    199     result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
    200     assert(SL_RESULT_SUCCESS == result);
    201     (void)result;
    202 
    203     // realize the output mix
    204     result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    205     assert(SL_RESULT_SUCCESS == result);
    206     (void)result;
    207 }
    208 
    209 void Java_org_chromium_latency_walt_AudioTest_destroyEngine(__attribute__((unused)) JNIEnv *env,
    210                                                             __attribute__((unused)) jclass clazz)
    211 {
    212     if (bqPlayerObject != NULL) {
    213         (*bqPlayerObject)->Destroy(bqPlayerObject);
    214         bqPlayerObject = NULL;
    215     }
    216 
    217     if (outputMixObject != NULL) {
    218         (*outputMixObject)->Destroy(outputMixObject);
    219         outputMixObject = NULL;
    220     }
    221 
    222     if (engineObject != NULL) {
    223         (*engineObject)->Destroy(engineObject);
    224         engineObject = NULL;
    225     }
    226 }
    227 
    228 // create buffer queue audio player
    229 void Java_org_chromium_latency_walt_AudioTest_createBufferQueueAudioPlayer(
    230         __attribute__((unused)) JNIEnv* env,
    231         __attribute__((unused)) jclass clazz,
    232         jint optimalFrameRate,
    233         jint optimalFramesPerBuffer)
    234 {
    235     __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Creating audio player with frame rate %d and frames per buffer %d",
    236                         optimalFrameRate, optimalFramesPerBuffer);
    237 
    238     framesPerBuffer = optimalFramesPerBuffer;
    239     createWaveTables();
    240 
    241     SLresult result;
    242 
    243     // configure the audio source (supply data through a buffer queue in PCM format)
    244     SLDataLocator_AndroidSimpleBufferQueue locator_bufferqueue_source;
    245     SLDataFormat_PCM format_pcm;
    246     SLDataSource audio_source;
    247 
    248     // source location
    249     locator_bufferqueue_source.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
    250     locator_bufferqueue_source.numBuffers = 1;
    251 
    252     // source format
    253     format_pcm.formatType = SL_DATAFORMAT_PCM;
    254     format_pcm.numChannels = 1;
    255 
    256     // Note: this shouldn't be called samplesPerSec it should be called *framesPerSec*
    257     // because when channels = 2 then there are 2 samples per frame.
    258     format_pcm.samplesPerSec = (SLuint32) optimalFrameRate * 1000;
    259     format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
    260     format_pcm.containerSize = 16;
    261     format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
    262     format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
    263 
    264     audio_source.pLocator = &locator_bufferqueue_source;
    265     audio_source.pFormat = &format_pcm;
    266 
    267     // configure the output: An output mix sink
    268     SLDataLocator_OutputMix locator_output_mix;
    269     SLDataSink audio_sink;
    270 
    271     locator_output_mix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
    272     locator_output_mix.outputMix = outputMixObject;
    273 
    274     audio_sink.pLocator = &locator_output_mix;
    275     audio_sink.pFormat = NULL;
    276 
    277     // create audio player
    278     // Note: Adding other output interfaces here will result in your audio being routed using the
    279     // normal path NOT the fast path
    280     const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
    281     const SLboolean interfaces_required[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
    282 
    283     result = (*engineEngine)->CreateAudioPlayer(
    284         engineEngine,
    285         &bqPlayerObject,
    286         &audio_source,
    287         &audio_sink,
    288         2, // Number of interfaces
    289         interface_ids,
    290         interfaces_required
    291     );
    292 
    293     assert(SL_RESULT_SUCCESS == result);
    294     (void)result;
    295 
    296     // realize the player
    297     result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
    298     assert(SL_RESULT_SUCCESS == result);
    299     (void)result;
    300 
    301     // get the play interface
    302     result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
    303     assert(SL_RESULT_SUCCESS == result);
    304     (void)result;
    305 
    306     // get the buffer queue interface
    307     result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
    308             &bqPlayerBufferQueue);
    309     assert(SL_RESULT_SUCCESS == result);
    310     (void)result;
    311 
    312     // register callback on the buffer queue
    313     result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
    314     assert(SL_RESULT_SUCCESS == result);
    315     (void)result;
    316 }
    317 
    318 void Java_org_chromium_latency_walt_AudioTest_startWarmTest(__attribute__((unused)) JNIEnv* env,
    319                                                             __attribute__((unused)) jclass clazz) {
    320     SLresult result;
    321 
    322     result = (*bqPlayerBufferQueue)->Clear(bqPlayerBufferQueue);
    323     assert(SL_RESULT_SUCCESS == result);
    324     (void)result;
    325 
    326     // enqueue some silence
    327     result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, silenceBuffer, bufferSizeInBytes);
    328     assert(SL_RESULT_SUCCESS == result);
    329     (void)result;
    330 
    331     // set the player's state to playing
    332     result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
    333     assert(SL_RESULT_SUCCESS == result);
    334     (void)result;
    335 
    336     warmedUp = 1;
    337 }
    338 
    339 void Java_org_chromium_latency_walt_AudioTest_stopTests(__attribute__((unused)) JNIEnv *env,
    340                                                         __attribute__((unused)) jclass clazz) {
    341     SLresult result;
    342 
    343     result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
    344     assert(SL_RESULT_SUCCESS == result);
    345     (void)result;
    346 
    347     warmedUp = 0;
    348 }
    349 
    350 // this callback handler is called every time a buffer finishes recording
    351 void bqRecorderCallback(__attribute__((unused)) SLAndroidSimpleBufferQueueItf bq,
    352                         __attribute__((unused)) void *context)
    353 {
    354     tc_rec = uptimeMicros();
    355     assert(bq == recorderBufferQueue);
    356     assert(NULL == context);
    357 
    358     // for streaming recording, here we would call Enqueue to give recorder the next buffer to fill
    359     // but instead, this is a one-time buffer so we stop recording
    360     SLresult result;
    361     result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
    362     if (SL_RESULT_SUCCESS == result) {
    363         recorderSize = recorder_frames * sizeof(short);
    364     }
    365     bqPlayerRecorderBusy = 0;
    366 
    367     //// TODO: Use small buffers and re-enqueue each time
    368     // result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recorderBuffer,
    369     //         recorder_frames * sizeof(short));
    370     // assert(SL_RESULT_SUCCESS == result);
    371 }
    372 
    373 // create audio recorder
    374 jboolean Java_org_chromium_latency_walt_AudioTest_createAudioRecorder(
    375         __attribute__((unused)) JNIEnv* env,
    376         __attribute__((unused)) jclass clazz,
    377         jint optimalFrameRate,
    378         jint framesToRecord)
    379 {
    380     SLresult result;
    381 
    382     __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Creating audio recorder with frame rate %d and frames to record %d",
    383                         optimalFrameRate, framesToRecord);
    384     // Allocate buffer
    385     recorder_frames = framesToRecord;
    386     recorderBuffer = malloc(sizeof(*recorderBuffer) * recorder_frames);
    387 
    388     // configure audio source
    389     SLDataLocator_IODevice loc_dev = {
    390             SL_DATALOCATOR_IODEVICE,
    391             SL_IODEVICE_AUDIOINPUT,
    392             SL_DEFAULTDEVICEID_AUDIOINPUT,
    393             NULL
    394         };
    395     SLDataSource audioSrc = {&loc_dev, NULL};
    396 
    397     // configure audio sink
    398     SLDataLocator_AndroidSimpleBufferQueue loc_bq;
    399     loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
    400     loc_bq.numBuffers = 2;
    401 
    402 
    403     // source format
    404     SLDataFormat_PCM format_pcm;
    405     format_pcm.formatType = SL_DATAFORMAT_PCM;
    406     format_pcm.numChannels = CHANNELS;
    407     // Note: this shouldn't be called samplesPerSec it should be called *framesPerSec*
    408     // because when channels = 2 then there are 2 samples per frame.
    409     format_pcm.samplesPerSec = (SLuint32) optimalFrameRate * 1000;
    410     format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
    411     format_pcm.containerSize = 16;
    412     format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
    413     format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
    414 
    415 
    416     SLDataSink audioSnk = {&loc_bq, &format_pcm};
    417 
    418     // create audio recorder
    419     // (requires the RECORD_AUDIO permission)
    420     const SLInterfaceID id[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
    421                                  SL_IID_ANDROIDCONFIGURATION };
    422     const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
    423     result = (*engineEngine)->CreateAudioRecorder(engineEngine,
    424                                               &recorderObject,
    425                                               &audioSrc,
    426                                               &audioSnk,
    427                                               sizeof(id)/sizeof(id[0]),
    428                                               id, req);
    429 
    430     // Configure the voice recognition preset which has no
    431     // signal processing for lower latency.
    432     SLAndroidConfigurationItf inputConfig;
    433     result = (*recorderObject)->GetInterface(recorderObject,
    434                                             SL_IID_ANDROIDCONFIGURATION,
    435                                             &inputConfig);
    436     if (SL_RESULT_SUCCESS == result) {
    437         SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
    438         (*inputConfig)->SetConfiguration(inputConfig,
    439                                          SL_ANDROID_KEY_RECORDING_PRESET,
    440                                          &presetValue,
    441                                          sizeof(SLuint32));
    442     }
    443 
    444     // realize the audio recorder
    445     result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
    446     if (SL_RESULT_SUCCESS != result) {
    447         return JNI_FALSE;
    448     }
    449 
    450     // get the record interface
    451     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
    452     assert(SL_RESULT_SUCCESS == result);
    453     (void)result;
    454 
    455     // get the buffer queue interface
    456     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
    457             &recorderBufferQueue);
    458     assert(SL_RESULT_SUCCESS == result);
    459     (void)result;
    460 
    461     // register callback on the buffer queue
    462     result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback,
    463             NULL);
    464     assert(SL_RESULT_SUCCESS == result);
    465     (void)result;
    466 
    467     __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Audio recorder created, buffer size: %d frames",
    468                         recorder_frames);
    469 
    470     return JNI_TRUE;
    471 }
    472 
    473 
    474 // set the recording state for the audio recorder
    475 void Java_org_chromium_latency_walt_AudioTest_startRecording(__attribute__((unused)) JNIEnv* env,
    476                                                              __attribute__((unused)) jclass clazz)
    477 {
    478     SLresult result;
    479 
    480     if( bqPlayerRecorderBusy) {
    481         return;
    482     }
    483     // in case already recording, stop recording and clear buffer queue
    484     result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
    485     assert(SL_RESULT_SUCCESS == result);
    486     (void)result;
    487     result = (*recorderBufferQueue)->Clear(recorderBufferQueue);
    488     assert(SL_RESULT_SUCCESS == result);
    489     (void)result;
    490 
    491     // the buffer is not valid for playback yet
    492     recorderSize = 0;
    493 
    494     // enqueue an empty buffer to be filled by the recorder
    495     // (for streaming recording, we would enqueue at least 2 empty buffers to start things off)
    496     te_rec = uptimeMicros();  // TODO: investigate if it's better to time after SetRecordState
    497     tc_rec = 0;
    498     result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recorderBuffer,
    499             recorder_frames * sizeof(short));
    500     // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
    501     // which for this code example would indicate a programming error
    502     assert(SL_RESULT_SUCCESS == result);
    503     (void)result;
    504 
    505     // start recording
    506     result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
    507     assert(SL_RESULT_SUCCESS == result);
    508     (void)result;
    509     bqPlayerRecorderBusy = 1;
    510 }
    511 
    512 jshortArray Java_org_chromium_latency_walt_AudioTest_getRecordedWave(
    513         JNIEnv *env,
    514         __attribute__((unused)) jclass cls)
    515 {
    516     jshortArray result;
    517     result = (*env)->NewShortArray(env, recorder_frames);
    518     if (result == NULL) {
    519         return NULL; /* out of memory error thrown */
    520     }
    521     (*env)->SetShortArrayRegion(env, result, 0, recorder_frames, recorderBuffer);
    522     return result;
    523 }
    524 
    525 jlong Java_org_chromium_latency_walt_AudioTest_getTcRec(__attribute__((unused)) JNIEnv *env,
    526                                                         __attribute__((unused)) jclass cls) {
    527     return (jlong) tc_rec;
    528 }
    529 
    530 jlong Java_org_chromium_latency_walt_AudioTest_getTeRec(__attribute__((unused)) JNIEnv *env,
    531                                                         __attribute__((unused)) jclass cls) {
    532     return (jlong) te_rec;
    533 }
    534 
    535 jlong Java_org_chromium_latency_walt_AudioTest_getTePlay(__attribute__((unused)) JNIEnv *env,
    536                                                          __attribute__((unused)) jclass cls) {
    537     return (jlong) te_play;
    538 }
    539