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
35static SLObjectItf engineObject = NULL;
36static SLEngineItf engineEngine = NULL;
37
38// output mix interfaces
39static SLObjectItf outputMixObject = NULL;
40
41// buffer queue player interfaces
42static SLObjectItf bqPlayerObject = NULL;
43static SLPlayItf bqPlayerPlay = NULL;
44static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
45
46// recorder interfaces
47static SLObjectItf recorderObject = NULL;
48static SLRecordItf recorderRecord;
49static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
50static volatile int bqPlayerRecorderBusy = 0;
51
52static unsigned int recorder_frames;
53static short* recorderBuffer;
54static unsigned recorderSize = 0;
55
56static unsigned int framesPerBuffer;
57
58#define CHANNELS 1  // 1 for mono, 2 for stereo
59
60// Each short represents a 16-bit audio sample
61static short* beepBuffer = NULL;
62static short* silenceBuffer = NULL;
63static 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
70static unsigned buffersRemaining = 0;
71static short warmedUp = 0;
72
73
74// Timestamps
75// te - enqueue time
76// tc - callback time
77int64_t te_play = 0, te_rec = 0, tc_rec = 0;
78
79/**
80 * Create wave tables for audio out.
81 */
82void 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
105void 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
139jlong 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
176void 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
209void 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
229void 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
318void 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
339void 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
351void 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
374jboolean 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
475void 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
512jshortArray 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
525jlong Java_org_chromium_latency_walt_AudioTest_getTcRec(__attribute__((unused)) JNIEnv *env,
526                                                        __attribute__((unused)) jclass cls) {
527    return (jlong) tc_rec;
528}
529
530jlong Java_org_chromium_latency_walt_AudioTest_getTeRec(__attribute__((unused)) JNIEnv *env,
531                                                        __attribute__((unused)) jclass cls) {
532    return (jlong) te_rec;
533}
534
535jlong Java_org_chromium_latency_walt_AudioTest_getTePlay(__attribute__((unused)) JNIEnv *env,
536                                                         __attribute__((unused)) jclass cls) {
537    return (jlong) te_play;
538}
539