1 /*
2 * Copyright (C) 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 // FIXME taken from OpenSLES_AndroidConfiguration.h
18 #define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode")
19
20 ////////////////////////////////////////////
21 /// Actual sles functions.
22
23
24 // Test program to record from default audio input and playback to default audio output.
25 // It will generate feedback (Larsen effect) if played through on-device speakers,
26 // or acts as a delay if played through headset.
27
28 #define _USE_MATH_DEFINES
29 #include <cmath>
30 #include "sles.h"
31 #include "audio_utils/atomic.h"
32 #include "byte_buffer.h"
33 #include <unistd.h>
34 #include <string.h>
35
36 static int slesCreateServer(sles_data *pSles, int samplingRate, int frameCount, int micSource,
37 int performanceMode,
38 int testType, double frequency1, char* byteBufferPtr, int byteBufferLength,
39 short* loopbackTone, int maxRecordedLateCallbacks, int ignoreFirstFrames);
40 static int slesDestroyServer(sles_data *pSles);
41
42 static void initBufferStats(bufferStats *stats);
43 static void collectBufferPeriod(bufferStats *stats, bufferStats *fdpStats,
44 callbackTimeStamps *timeStamps, short expectedBufferPeriod);
45 static bool updateBufferStats(bufferStats *stats, int64_t diff_in_nano, int expectedBufferPeriod);
46 static void recordTimeStamp(callbackTimeStamps *timeStamps,
47 int64_t callbackDuration, int64_t timeStamp);
48
slesComputeDefaultSettings(int,int *,int *,int *)49 int slesComputeDefaultSettings(int /*performanceMode*/, int* /*samplingRate*/,
50 int* /*playerBufferFrameCount*/, int* /*recorderBufferFrameCount*/) {
51 // For OpenSL ES, these parameters can be determined by NativeAudioThread itself.
52 return STATUS_FAIL;
53 }
54
slesInit(void ** ppCtx,int samplingRate,int frameCount,int micSource,int performanceMode,int testType,double frequency1,char * byteBufferPtr,int byteBufferLength,short * loopbackTone,int maxRecordedLateCallbacks,int ignoreFirstFrames)55 int slesInit(void ** ppCtx, int samplingRate, int frameCount, int micSource,
56 int performanceMode,
57 int testType, double frequency1, char* byteBufferPtr, int byteBufferLength,
58 short* loopbackTone, int maxRecordedLateCallbacks, int ignoreFirstFrames) {
59 sles_data ** ppSles = (sles_data**) ppCtx;
60 int status = STATUS_FAIL;
61 if (ppSles != NULL) {
62 sles_data * pSles = (sles_data*) calloc(1, sizeof(sles_data));
63
64 SLES_PRINTF("pSles malloc %zu bytes at %p", sizeof(sles_data), pSles);
65 //__android_log_print(ANDROID_LOG_INFO, "sles_jni",
66 //"malloc %d bytes at %p", sizeof(sles_data), pSles);//Or ANDROID_LOG_INFO, ...
67 *ppSles = pSles;
68 if (pSles != NULL)
69 {
70 SLES_PRINTF("creating server. Sampling rate =%d, frame count = %d",
71 samplingRate, frameCount);
72 status = slesCreateServer(pSles, samplingRate, frameCount, micSource,
73 performanceMode, testType,
74 frequency1, byteBufferPtr, byteBufferLength, loopbackTone,
75 maxRecordedLateCallbacks, ignoreFirstFrames);
76 SLES_PRINTF("slesCreateServer =%d", status);
77 }
78 }
79
80 return status;
81 }
slesDestroy(void ** ppCtx)82 int slesDestroy(void ** ppCtx) {
83 sles_data ** ppSles = (sles_data**)ppCtx;
84 int status = STATUS_FAIL;
85 if (ppSles != NULL) {
86 slesDestroyServer(*ppSles);
87
88 if (*ppSles != NULL)
89 {
90 SLES_PRINTF("free memory at %p",*ppSles);
91 free(*ppSles);
92 *ppSles = 0;
93 }
94 status = STATUS_SUCCESS;
95 }
96 return status;
97 }
98
99 #define ASSERT(x) do { if(!(x)) { __android_log_assert("assert", "sles_jni", \
100 "ASSERTION FAILED: " #x); } } while (0)
101 #define ASSERT_EQ(x, y) do { if ((x) == (y)) ; else __android_log_assert("assert", "sles_jni", \
102 "ASSERTION FAILED: 0x%x != 0x%x\n", (unsigned) (x), (unsigned) (y)); } while (0)
103
104 // Called after audio recorder fills a buffer with data, then we can read from this filled buffer
recorderCallback(SLAndroidSimpleBufferQueueItf caller __unused,void * context)105 static void recorderCallback(SLAndroidSimpleBufferQueueItf caller __unused, void *context) {
106 sles_data *pSles = (sles_data*) context;
107 if (pSles != NULL) {
108 collectBufferPeriod(&pSles->recorderBufferStats, NULL /*fdpStats*/,
109 &pSles->recorderTimeStamps, pSles->expectedBufferPeriod);
110
111 //__android_log_print(ANDROID_LOG_INFO, "sles_jni", "in recorderCallback");
112 SLresult result;
113
114 //ee SLES_PRINTF("<R");
115
116 // We should only be called when a recording buffer is done
117 ASSERT(pSles->rxFront <= pSles->rxBufCount);
118 ASSERT(pSles->rxRear <= pSles->rxBufCount);
119 ASSERT(pSles->rxFront != pSles->rxRear);
120 char *buffer = pSles->rxBuffers[pSles->rxFront]; //pSles->rxBuffers stores the data recorded
121
122
123 // Remove buffer from record queue
124 if (++pSles->rxFront > pSles->rxBufCount) {
125 pSles->rxFront = 0;
126 }
127
128 if (pSles->testType == TEST_TYPE_LATENCY) {
129 // Throw out first frames
130 if (pSles->ignoreFirstFrames) {
131 int framesToErase = pSles->ignoreFirstFrames;
132 if (framesToErase > (int) pSles->bufSizeInFrames) {
133 framesToErase = pSles->bufSizeInFrames;
134 }
135 pSles->ignoreFirstFrames -= framesToErase;
136 memset(buffer, 0, framesToErase * pSles->channels * sizeof(short));
137 }
138
139 ssize_t actual = audio_utils_fifo_write(&(pSles->fifo), buffer,
140 (size_t) pSles->bufSizeInFrames);
141
142 if (actual != (ssize_t) pSles->bufSizeInFrames) {
143 write(1, "?", 1);
144 }
145
146 // This is called by a realtime (SCHED_FIFO) thread,
147 // and it is unsafe to do I/O as it could block for unbounded time.
148 // Flash filesystem is especially notorious for blocking.
149 if (pSles->fifo2Buffer != NULL) {
150 actual = audio_utils_fifo_write(&(pSles->fifo2), buffer,
151 (size_t) pSles->bufSizeInFrames);
152 if (actual != (ssize_t) pSles->bufSizeInFrames) {
153 write(1, "?", 1);
154 }
155 }
156 } else if (pSles->testType == TEST_TYPE_BUFFER_PERIOD) {
157 if (pSles->fifo2Buffer != NULL) {
158 ssize_t actual = byteBuffer_write(pSles->byteBufferPtr, pSles->byteBufferLength,
159 buffer, (size_t) pSles->bufSizeInFrames, pSles->channels);
160
161 //FIXME should log errors using other methods instead of printing to terminal
162 if (actual != (ssize_t) pSles->bufSizeInFrames) {
163 write(1, "?", 1);
164 }
165 }
166 }
167
168
169 // Enqueue this same buffer for the recorder to fill again.
170 result = (*(pSles->recorderBufferQueue))->Enqueue(pSles->recorderBufferQueue, buffer,
171 pSles->bufSizeInBytes);
172 //__android_log_print(ANDROID_LOG_INFO, "recorderCallback", "recorder buffer size: %i",
173 // pSles->bufSizeInBytes);
174 ASSERT_EQ(SL_RESULT_SUCCESS, result);
175
176
177 // Update our model of the record queue
178 SLuint32 rxRearNext = pSles->rxRear + 1;
179 if (rxRearNext > pSles->rxBufCount) {
180 rxRearNext = 0;
181 }
182 ASSERT(rxRearNext != pSles->rxFront);
183 pSles->rxBuffers[pSles->rxRear] = buffer;
184 pSles->rxRear = rxRearNext;
185
186
187
188 //ee SLES_PRINTF("r>");
189
190 } //pSles not null
191 }
192
193
194 // Calculate nanosecond difference between two timespec structs from clock_gettime(CLOCK_MONOTONIC)
195 // tv_sec [0, max time_t] , tv_nsec [0, 999999999]
diffInNano(struct timespec previousTime,struct timespec currentTime)196 static int64_t diffInNano(struct timespec previousTime, struct timespec currentTime) {
197 return (int64_t) (currentTime.tv_sec - previousTime.tv_sec) * (int64_t) NANOS_PER_SECOND +
198 currentTime.tv_nsec - previousTime.tv_nsec;
199 }
200
201 // Called after audio player empties a buffer of data
playerCallback(SLBufferQueueItf caller __unused,void * context)202 static void playerCallback(SLBufferQueueItf caller __unused, void *context) {
203 sles_data *pSles = (sles_data*) context;
204 if (pSles != NULL) {
205 collectBufferPeriod(&pSles->playerBufferStats, &pSles->recorderBufferStats /*fdpStats*/,
206 &pSles->playerTimeStamps, pSles->expectedBufferPeriod);
207 SLresult result;
208
209 //ee SLES_PRINTF("<P");
210
211 // Get the buffer that just finished playing
212 ASSERT(pSles->txFront <= pSles->txBufCount);
213 ASSERT(pSles->txRear <= pSles->txBufCount);
214 ASSERT(pSles->txFront != pSles->txRear);
215 char *buffer = pSles->txBuffers[pSles->txFront];
216 if (++pSles->txFront > pSles->txBufCount) {
217 pSles->txFront = 0;
218 }
219
220 if (pSles->testType == TEST_TYPE_LATENCY) {
221 // Jitter buffer should have strictly less than 2 buffers worth of data in it.
222 // This is to prevent the test itself from adding too much latency.
223 size_t discardedInputFrames = 0;
224 for (;;) {
225 size_t availToRead = audio_utils_fifo_availToRead(&pSles->fifo);
226 if (availToRead < pSles->bufSizeInFrames * 2) {
227 break;
228 }
229 ssize_t actual = audio_utils_fifo_read(&pSles->fifo, buffer,
230 pSles->bufSizeInFrames);
231 if (actual > 0) {
232 discardedInputFrames += actual;
233 }
234 if (actual != (ssize_t) pSles->bufSizeInFrames) {
235 break;
236 }
237 }
238 if (discardedInputFrames > 0) {
239 if (pSles->totalDiscardedInputFrames > 0) {
240 __android_log_print(ANDROID_LOG_WARN, "sles_jni",
241 "Discarded an additional %zu input frames after a total of %zu input frames"
242 " had previously been discarded",
243 discardedInputFrames, pSles->totalDiscardedInputFrames);
244 }
245 pSles->totalDiscardedInputFrames += discardedInputFrames;
246 }
247
248 ssize_t actual = audio_utils_fifo_read(&(pSles->fifo), buffer, pSles->bufSizeInFrames);
249 if (actual != (ssize_t) pSles->bufSizeInFrames) {
250 write(1, "/", 1);
251 // on underrun from pipe, substitute silence
252 memset(buffer, 0, pSles->bufSizeInFrames * pSles->channels * sizeof(short));
253 }
254
255 if (pSles->injectImpulse == -1) { // here we inject pulse
256
257 /*// Experimentally, a single frame impulse was insufficient to trigger feedback.
258 // Also a Nyquist frequency signal was also insufficient, probably because
259 // the response of output and/or input path was not adequate at high frequencies.
260 // This short burst of a few cycles of square wave at Nyquist/4 found to work well.
261 for (unsigned i = 0; i < pSles->bufSizeInFrames / 8; i += 8) {
262 for (int j = 0; j < 8; j++) {
263 for (unsigned k = 0; k < pSles->channels; k++) {
264 ((short *) buffer)[(i + j) * pSles->channels + k] =
265 j < 4 ? 0x7FFF : 0x8000;
266 }
267 }
268 }*/
269
270 //inject java generated tone
271 for (unsigned i = 0; i < pSles->bufSizeInFrames; ++i) {
272 for (unsigned k = 0; k < pSles->channels; ++k) {
273 ((short *) buffer)[i * pSles->channels + k] = pSles->loopbackTone[i];
274 }
275 }
276
277 pSles->injectImpulse = 0;
278 pSles->totalDiscardedInputFrames = 0;
279 }
280 } else if (pSles->testType == TEST_TYPE_BUFFER_PERIOD) {
281 double twoPi = M_PI * 2;
282 int maxShort = 32767;
283 float amplitude = 0.8;
284 short value;
285 double phaseIncrement = pSles->frequency1 / pSles->sampleRate;
286 bool isGlitchEnabled = false;
287 for (unsigned i = 0; i < pSles->bufSizeInFrames; i++) {
288 value = (short) (sin(pSles->bufferTestPhase1) * maxShort * amplitude);
289 for (unsigned k = 0; k < pSles->channels; ++k) {
290 ((short *) buffer)[i* pSles->channels + k] = value;
291 }
292
293 pSles->bufferTestPhase1 += twoPi * phaseIncrement;
294 // insert glitches if isGlitchEnabled == true, and insert it for every second
295 if (isGlitchEnabled && (pSles->count % pSles->sampleRate == 0)) {
296 pSles->bufferTestPhase1 += twoPi * phaseIncrement;
297 }
298
299 pSles->count++;
300
301 while (pSles->bufferTestPhase1 > twoPi) {
302 pSles->bufferTestPhase1 -= twoPi;
303 }
304 }
305 }
306
307 // Enqueue the filled buffer for playing
308 result = (*(pSles->playerBufferQueue))->Enqueue(pSles->playerBufferQueue, buffer,
309 pSles->bufSizeInBytes);
310 ASSERT_EQ(SL_RESULT_SUCCESS, result);
311
312 // Update our model of the player queue
313 ASSERT(pSles->txFront <= pSles->txBufCount);
314 ASSERT(pSles->txRear <= pSles->txBufCount);
315 SLuint32 txRearNext = pSles->txRear + 1;
316 if (txRearNext > pSles->txBufCount) {
317 txRearNext = 0;
318 }
319 ASSERT(txRearNext != pSles->txFront);
320 pSles->txBuffers[pSles->txRear] = buffer;
321 pSles->txRear = txRearNext;
322
323 } //pSles not null
324 }
325
326 // Used to set initial values for the bufferStats struct before values can be recorded.
initBufferStats(bufferStats * stats)327 static void initBufferStats(bufferStats *stats) {
328 stats->buffer_period = new int[RANGE](); // initialized to zeros
329 stats->previous_time = {0,0};
330 stats->current_time = {0,0};
331
332 stats->buffer_count = 0;
333 stats->max_buffer_period = 0;
334
335 stats->measurement_count = 0;
336 stats->SDM = 0;
337 stats->var = 0;
338 }
339
340 // Called in the beginning of playerCallback() to collect the interval between each callback.
341 // fdpStats is either NULL or a pointer to the buffer statistics for the full-duplex partner.
collectBufferPeriod(bufferStats * stats,bufferStats * fdpStats,callbackTimeStamps * timeStamps,short expectedBufferPeriod)342 static void collectBufferPeriod(bufferStats *stats, bufferStats *fdpStats,
343 callbackTimeStamps *timeStamps, short expectedBufferPeriod) {
344 clock_gettime(CLOCK_MONOTONIC, &(stats->current_time));
345
346 if (timeStamps->startTime.tv_sec == 0 && timeStamps->startTime.tv_nsec == 0) {
347 timeStamps->startTime = stats->current_time;
348 }
349
350 (stats->buffer_count)++;
351
352 if (stats->previous_time.tv_sec != 0 && stats->buffer_count > BUFFER_PERIOD_DISCARD &&
353 (fdpStats == NULL || fdpStats->buffer_count > BUFFER_PERIOD_DISCARD_FULL_DUPLEX_PARTNER)) {
354
355 int64_t callbackDuration = diffInNano(stats->previous_time, stats->current_time);
356
357 bool outlier = updateBufferStats(stats, callbackDuration, expectedBufferPeriod);
358
359 //recording timestamps of buffer periods not at expected buffer period
360 if (outlier) {
361 int64_t timeStamp = diffInNano(timeStamps->startTime, stats->current_time);
362 recordTimeStamp(timeStamps, callbackDuration, timeStamp);
363 }
364 }
365
366 stats->previous_time = stats->current_time;
367 }
368
369 // Records an outlier given the duration in nanoseconds and the number of nanoseconds
370 // between it and the start of the test.
recordTimeStamp(callbackTimeStamps * timeStamps,int64_t callbackDuration,int64_t timeStamp)371 static void recordTimeStamp(callbackTimeStamps *timeStamps,
372 int64_t callbackDuration, int64_t timeStamp) {
373 if (timeStamps->exceededCapacity) {
374 return;
375 }
376
377 //only marked as exceeded if attempting to record a late callback after arrays full
378 if (timeStamps->index == timeStamps->capacity){
379 timeStamps->exceededCapacity = true;
380 } else {
381 timeStamps->callbackDurations[timeStamps->index] =
382 (short) ((callbackDuration + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI);
383 timeStamps->timeStampsMs[timeStamps->index] =
384 (int) ((timeStamp + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI);
385 timeStamps->index++;
386 }
387 }
388
atomicSetIfGreater(volatile int32_t * addr,int32_t val)389 static void atomicSetIfGreater(volatile int32_t *addr, int32_t val) {
390 // TODO: rewrite this to avoid the need for unbounded spinning
391 int32_t old;
392 do {
393 old = *addr;
394 if (val < old) return;
395 } while(!android_atomic_compare_exchange(&old, val, addr));
396 }
397
398 // Updates the stats being collected about buffer periods. Returns true if this is an outlier.
updateBufferStats(bufferStats * stats,int64_t diff_in_nano,int expectedBufferPeriod)399 static bool updateBufferStats(bufferStats *stats, int64_t diff_in_nano, int expectedBufferPeriod) {
400 stats->measurement_count++;
401
402 // round up to nearest millisecond
403 int diff_in_milli = (int) ((diff_in_nano + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI);
404
405 if (diff_in_milli > stats->max_buffer_period) {
406 stats->max_buffer_period = diff_in_milli;
407 }
408
409 // from 0 ms to 1000 ms, plus a sum of all instances > 1000ms
410 if (diff_in_milli >= (RANGE - 1)) {
411 (stats->buffer_period)[RANGE-1]++;
412 } else if (diff_in_milli >= 0) {
413 (stats->buffer_period)[diff_in_milli]++;
414 } else { // for diff_in_milli < 0
415 __android_log_print(ANDROID_LOG_INFO, "sles_player", "Having negative BufferPeriod.");
416 }
417
418 int64_t delta = diff_in_nano - (int64_t) expectedBufferPeriod * NANOS_PER_MILLI;
419 stats->SDM += delta * delta;
420 if (stats->measurement_count > 1) {
421 stats->var = stats->SDM / stats->measurement_count;
422 }
423
424 // check if the lateness is so bad that a systrace should be captured
425 // TODO: replace static threshold of lateness with a dynamic determination
426 if (diff_in_milli > expectedBufferPeriod + LATE_CALLBACK_CAPTURE_THRESHOLD) {
427 // TODO: log in a non-blocking way
428 //__android_log_print(ANDROID_LOG_INFO, "buffer_stats", "Callback late by %d ms",
429 // diff_in_milli - expectedBufferPeriod);
430 atomicSetIfGreater(&(stats->captureRank), diff_in_milli - expectedBufferPeriod);
431 }
432 return diff_in_milli > expectedBufferPeriod + LATE_CALLBACK_OUTLIER_THRESHOLD;
433 }
434
slesCreateServer(sles_data * pSles,int samplingRate,int frameCount,int micSource,int performanceMode,int testType,double frequency1,char * byteBufferPtr,int byteBufferLength,short * loopbackTone,int maxRecordedLateCallbacks,int ignoreFirstFrames)435 static int slesCreateServer(sles_data *pSles, int samplingRate, int frameCount, int micSource,
436 int performanceMode,
437 int testType, double frequency1, char *byteBufferPtr, int byteBufferLength,
438 short *loopbackTone, int maxRecordedLateCallbacks, int ignoreFirstFrames) {
439 int status = STATUS_FAIL;
440
441 if (pSles != NULL) {
442
443 // adb shell slesTest_feedback -r1 -t1 -s48000 -f240 -i300 -e3 -o/sdcard/log.wav
444 // r1 and t1 are the receive and transmit buffer counts, typically 1
445 // s is the sample rate, typically 48000 or 44100
446 // f is the frame count per buffer, typically 240 or 256
447 // i is the number of milliseconds before impulse. You may need to adjust this.
448 // e is number of seconds to record
449 // o is output .wav file name
450
451
452 // // default values
453 // SLuint32 rxBufCount = 1; // -r#
454 // SLuint32 txBufCount = 1; // -t#
455 // SLuint32 bufSizeInFrames = 240; // -f#
456 // SLuint32 channels = 1; // -c#
457 // SLuint32 sampleRate = 48000; // -s#
458 // SLuint32 exitAfterSeconds = 3; // -e#
459 // SLuint32 freeBufCount = 0; // calculated
460 // SLuint32 bufSizeInBytes = 0; // calculated
461 // int injectImpulse = 300; // -i#i
462 //
463 // // Storage area for the buffer queues
464 // char **rxBuffers;
465 // char **txBuffers;
466 // char **freeBuffers;
467 //
468 // // Buffer indices
469 // SLuint32 rxFront; // oldest recording
470 // SLuint32 rxRear; // next to be recorded
471 // SLuint32 txFront; // oldest playing
472 // SLuint32 txRear; // next to be played
473 // SLuint32 freeFront; // oldest free
474 // SLuint32 freeRear; // next to be freed
475 //
476 // audio_utils_fifo fifo; //(*)
477 // SLAndroidSimpleBufferQueueItf recorderBufferQueue;
478 // SLBufferQueueItf playerBufferQueue;
479
480 // default values
481 pSles->rxBufCount = 1; // -r#
482 pSles->txBufCount = 1; // -t#
483 pSles->bufSizeInFrames = frameCount;//240; // -f#
484 pSles->channels = 1; // -c#
485 pSles->sampleRate = samplingRate;//48000; // -s#
486 pSles->exitAfterSeconds = 3; // -e#
487 pSles->freeBufCount = 0; // calculated
488 pSles->bufSizeInBytes = 0; // calculated
489 pSles->injectImpulse = 300; // -i#i
490 pSles->totalDiscardedInputFrames = 0;
491 pSles->ignoreFirstFrames = ignoreFirstFrames;
492
493 // Storage area for the buffer queues
494 // char **rxBuffers;
495 // char **txBuffers;
496 // char **freeBuffers;
497
498 // Buffer indices
499 #if 0
500 pSles->rxFront; // oldest recording
501 pSles->rxRear; // next to be recorded
502 pSles->txFront; // oldest playing
503 pSles->txRear; // next to be played
504 pSles->freeFront; // oldest free
505 pSles->freeRear; // next to be freed
506
507 pSles->fifo; //(*)
508 #endif
509 pSles->fifo2Buffer = NULL; //this fifo is for sending data to java code (to plot it)
510 #if 0
511 pSles->recorderBufferQueue;
512 pSles->playerBufferQueue;
513 #endif
514
515
516
517 // compute total free buffers as -r plus -t
518 pSles->freeBufCount = pSles->rxBufCount + pSles->txBufCount;
519 // compute buffer size
520 pSles->bufSizeInBytes = pSles->channels * pSles->bufSizeInFrames * sizeof(short);
521
522 // Initialize free buffers
523 pSles->freeBuffers = (char **) calloc(pSles->freeBufCount + 1, sizeof(char *));
524 SLES_PRINTF(" calloc freeBuffers %llu bytes at %p", (long long)pSles->freeBufCount + 1,
525 pSles->freeBuffers);
526 unsigned j;
527 for (j = 0; j < pSles->freeBufCount; ++j) {
528 pSles->freeBuffers[j] = (char *) malloc(pSles->bufSizeInBytes);
529 SLES_PRINTF(" buff%d malloc %llu bytes at %p",j, (long long)pSles->bufSizeInBytes,
530 pSles->freeBuffers[j]);
531 }
532 pSles->freeFront = 0;
533 pSles->freeRear = pSles->freeBufCount;
534 pSles->freeBuffers[j] = NULL;
535
536 // Initialize record queue
537 pSles->rxBuffers = (char **) calloc(pSles->rxBufCount + 1, sizeof(char *));
538 SLES_PRINTF(" calloc rxBuffers %llu bytes at %p", (long long)pSles->rxBufCount + 1,
539 pSles->rxBuffers);
540 pSles->rxFront = 0;
541 pSles->rxRear = 0;
542
543 // Initialize play queue
544 pSles->txBuffers = (char **) calloc(pSles->txBufCount + 1, sizeof(char *));
545 SLES_PRINTF(" calloc txBuffers %llu bytes at %p", (long long)pSles->txBufCount + 1,
546 pSles->txBuffers);
547 pSles->txFront = 0;
548 pSles->txRear = 0;
549
550 size_t frameSize = pSles->channels * sizeof(short);
551 #define FIFO_FRAMES 1024
552 pSles->fifoBuffer = new short[FIFO_FRAMES * pSles->channels];
553 audio_utils_fifo_init(&(pSles->fifo), FIFO_FRAMES, frameSize, pSles->fifoBuffer);
554
555 // SNDFILE *sndfile;
556 // if (outFileName != NULL) {
557 // create .wav writer
558 // SF_INFO info;
559 // info.frames = 0;
560 // info.samplerate = sampleRate;
561 // info.channels = channels;
562 // info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
563 // sndfile = sf_open(outFileName, SFM_WRITE, &info);
564 // if (sndfile != NULL) {
565 #define FIFO2_FRAMES 65536
566 pSles->fifo2Buffer = new short[FIFO2_FRAMES * pSles->channels];
567 audio_utils_fifo_init(&(pSles->fifo2), FIFO2_FRAMES, frameSize, pSles->fifo2Buffer);
568 // } else {
569 // fprintf(stderr, "sf_open failed\n");
570 // }
571 // } else {
572 // sndfile = NULL;
573 // }
574
575 initBufferStats(&pSles->recorderBufferStats);
576 initBufferStats(&pSles->playerBufferStats);
577
578 // init other variables needed for buffer test
579 pSles->testType = testType;
580 pSles->frequency1 = frequency1;
581 pSles->bufferTestPhase1 = 0;
582 pSles->count = 0;
583 pSles->byteBufferPtr = byteBufferPtr;
584 pSles->byteBufferLength = byteBufferLength;
585
586 //init loopback tone
587 pSles->loopbackTone = loopbackTone;
588
589 pSles->recorderTimeStamps = {
590 new int[maxRecordedLateCallbacks], //int* timeStampsMs
591 new short[maxRecordedLateCallbacks], //short* callbackDurations
592 0, //short index
593 {0,0}, //struct timespec startTime;
594 maxRecordedLateCallbacks, //int capacity
595 false //bool exceededCapacity
596 };
597
598 pSles->playerTimeStamps = {
599 new int[maxRecordedLateCallbacks], //int* timeStampsMs
600 new short[maxRecordedLateCallbacks], //short* callbackDurations;
601 0, //short index
602 {0,0}, //struct timespec startTime;
603 maxRecordedLateCallbacks, //int capacity
604 false //bool exceededCapacity
605 };
606
607 pSles->expectedBufferPeriod = (short) (
608 round(pSles->bufSizeInFrames * MILLIS_PER_SECOND / (float) pSles->sampleRate));
609
610 SLresult result;
611
612 // create engine
613 #if 0
614 pSles->engineObject;
615 #endif
616 result = slCreateEngine(&(pSles->engineObject), 0, NULL, 0, NULL, NULL);
617 ASSERT_EQ(SL_RESULT_SUCCESS, result);
618 result = (*(pSles->engineObject))->Realize(pSles->engineObject, SL_BOOLEAN_FALSE);
619 ASSERT_EQ(SL_RESULT_SUCCESS, result);
620 SLEngineItf engineEngine;
621 result = (*(pSles->engineObject))->GetInterface(pSles->engineObject, SL_IID_ENGINE,
622 &engineEngine);
623 ASSERT_EQ(SL_RESULT_SUCCESS, result);
624
625 // create output mix
626 #if 0
627 pSles->outputmixObject;
628 #endif
629 result = (*engineEngine)->CreateOutputMix(engineEngine, &(pSles->outputmixObject), 0, NULL,
630 NULL);
631 ASSERT_EQ(SL_RESULT_SUCCESS, result);
632 result = (*(pSles->outputmixObject))->Realize(pSles->outputmixObject, SL_BOOLEAN_FALSE);
633 ASSERT_EQ(SL_RESULT_SUCCESS, result);
634
635 // create an audio player with buffer queue source and output mix sink
636 SLDataSource audiosrc;
637 SLDataSink audiosnk;
638 SLDataFormat_PCM pcm;
639 SLDataLocator_OutputMix locator_outputmix;
640 SLDataLocator_BufferQueue locator_bufferqueue_tx;
641 locator_bufferqueue_tx.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
642 locator_bufferqueue_tx.numBuffers = pSles->txBufCount;
643 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
644 locator_outputmix.outputMix = pSles->outputmixObject;
645 pcm.formatType = SL_DATAFORMAT_PCM;
646 pcm.numChannels = pSles->channels;
647 pcm.samplesPerSec = pSles->sampleRate * 1000;
648 pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
649 pcm.containerSize = 16;
650 pcm.channelMask = pSles->channels == 1 ? SL_SPEAKER_FRONT_CENTER :
651 (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
652 pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
653 audiosrc.pLocator = &locator_bufferqueue_tx;
654 audiosrc.pFormat = &pcm;
655 audiosnk.pLocator = &locator_outputmix;
656 audiosnk.pFormat = NULL;
657 pSles->playerObject = NULL;
658 pSles->recorderObject = NULL;
659 SLInterfaceID ids_tx[2] = {SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
660 SLboolean flags_tx[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
661 result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(pSles->playerObject),
662 &audiosrc, &audiosnk, 2, ids_tx, flags_tx);
663 if (SL_RESULT_CONTENT_UNSUPPORTED == result) {
664 SLES_PRINTF("ERROR: Could not create audio player (result %x), check sample rate\n",
665 result);
666 goto cleanup;
667 }
668 ASSERT_EQ(SL_RESULT_SUCCESS, result);
669
670 {
671 /* Get the Android configuration interface which is explicit */
672 SLAndroidConfigurationItf configItf;
673 result = (*(pSles->playerObject))->GetInterface(pSles->playerObject,
674 SL_IID_ANDROIDCONFIGURATION, (void*)&configItf);
675 ASSERT_EQ(SL_RESULT_SUCCESS, result);
676
677 /* Use the configuration interface to configure the player before it's realized */
678 if (performanceMode != -1) {
679 SLuint32 performanceMode32 = performanceMode;
680 result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE,
681 &performanceMode32, sizeof(SLuint32));
682 ASSERT_EQ(SL_RESULT_SUCCESS, result);
683 }
684
685 }
686
687 result = (*(pSles->playerObject))->Realize(pSles->playerObject, SL_BOOLEAN_FALSE);
688 ASSERT_EQ(SL_RESULT_SUCCESS, result);
689 SLPlayItf playerPlay;
690 result = (*(pSles->playerObject))->GetInterface(pSles->playerObject, SL_IID_PLAY,
691 &playerPlay);
692 ASSERT_EQ(SL_RESULT_SUCCESS, result);
693 result = (*(pSles->playerObject))->GetInterface(pSles->playerObject, SL_IID_BUFFERQUEUE,
694 &(pSles->playerBufferQueue));
695 ASSERT_EQ(SL_RESULT_SUCCESS, result);
696 result = (*(pSles->playerBufferQueue))->RegisterCallback(pSles->playerBufferQueue,
697 playerCallback, pSles); //playerCallback is the name of callback function
698 ASSERT_EQ(SL_RESULT_SUCCESS, result);
699
700 // Enqueue some zero buffers for the player
701 for (j = 0; j < pSles->txBufCount; ++j) {
702
703 // allocate a free buffer
704 ASSERT(pSles->freeFront != pSles->freeRear);
705 char *buffer = pSles->freeBuffers[pSles->freeFront];
706 if (++pSles->freeFront > pSles->freeBufCount) {
707 pSles->freeFront = 0;
708 }
709
710 // put on play queue
711 SLuint32 txRearNext = pSles->txRear + 1;
712 if (txRearNext > pSles->txBufCount) {
713 txRearNext = 0;
714 }
715 ASSERT(txRearNext != pSles->txFront);
716 pSles->txBuffers[pSles->txRear] = buffer;
717 pSles->txRear = txRearNext;
718 result = (*(pSles->playerBufferQueue))->Enqueue(pSles->playerBufferQueue,
719 buffer, pSles->bufSizeInBytes);
720 ASSERT_EQ(SL_RESULT_SUCCESS, result);
721 }
722
723 result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
724 ASSERT_EQ(SL_RESULT_SUCCESS, result);
725
726 // Create an audio recorder with microphone device source and buffer queue sink.
727 // The buffer queue as sink is an Android-specific extension.
728 SLDataLocator_IODevice locator_iodevice;
729 SLDataLocator_AndroidSimpleBufferQueue locator_bufferqueue_rx;
730
731 locator_iodevice.locatorType = SL_DATALOCATOR_IODEVICE;
732 locator_iodevice.deviceType = SL_IODEVICE_AUDIOINPUT;
733 locator_iodevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
734 locator_iodevice.device = NULL;
735
736 audiosrc.pLocator = &locator_iodevice;
737 audiosrc.pFormat = NULL;
738
739 locator_bufferqueue_rx.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
740 locator_bufferqueue_rx.numBuffers = pSles->rxBufCount;
741
742 audiosnk.pLocator = &locator_bufferqueue_rx;
743 audiosnk.pFormat = &pcm;
744
745 { //why brackets here?
746 SLInterfaceID ids_rx[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
747 SL_IID_ANDROIDCONFIGURATION};
748 SLboolean flags_rx[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
749 result = (*engineEngine)->CreateAudioRecorder(engineEngine, &(pSles->recorderObject),
750 &audiosrc, &audiosnk, 2, ids_rx, flags_rx);
751 if (SL_RESULT_SUCCESS != result) {
752 status = STATUS_FAIL;
753
754 SLES_PRINTF("ERROR: Could not create audio recorder (result %x), "
755 "check sample rate and channel count\n", result);
756 goto cleanup;
757 }
758 }
759 ASSERT_EQ(SL_RESULT_SUCCESS, result);
760
761 {
762 /* Get the Android configuration interface which is explicit */
763 SLAndroidConfigurationItf configItf;
764 result = (*(pSles->recorderObject))->GetInterface(pSles->recorderObject,
765 SL_IID_ANDROIDCONFIGURATION, (void*)&configItf);
766 ASSERT_EQ(SL_RESULT_SUCCESS, result);
767
768 SLuint32 presetValue = micSource;
769 //SL_ANDROID_RECORDING_PRESET_CAMCORDER;//SL_ANDROID_RECORDING_PRESET_NONE;
770
771 /* Use the configuration interface to configure the recorder before it's realized */
772 if (presetValue != SL_ANDROID_RECORDING_PRESET_NONE) {
773 result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
774 &presetValue, sizeof(SLuint32));
775 ASSERT_EQ(SL_RESULT_SUCCESS, result);
776 }
777 if (performanceMode != -1) {
778 SLuint32 performanceMode32 = performanceMode;
779 result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE,
780 &performanceMode32, sizeof(SLuint32));
781 ASSERT_EQ(SL_RESULT_SUCCESS, result);
782 }
783
784 }
785
786 result = (*(pSles->recorderObject))->Realize(pSles->recorderObject, SL_BOOLEAN_FALSE);
787 ASSERT_EQ(SL_RESULT_SUCCESS, result);
788
789 SLRecordItf recorderRecord;
790 result = (*(pSles->recorderObject))->GetInterface(pSles->recorderObject, SL_IID_RECORD,
791 &recorderRecord);
792 ASSERT_EQ(SL_RESULT_SUCCESS, result);
793
794 result = (*(pSles->recorderObject))->GetInterface(pSles->recorderObject,
795 SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(pSles->recorderBufferQueue));
796 ASSERT_EQ(SL_RESULT_SUCCESS, result);
797
798 result = (*(pSles->recorderBufferQueue))->RegisterCallback(pSles->recorderBufferQueue,
799 recorderCallback, pSles);
800 ASSERT_EQ(SL_RESULT_SUCCESS, result);
801
802 // Enqueue some empty buffers for the recorder
803 for (j = 0; j < pSles->rxBufCount; ++j) {
804
805 // allocate a free buffer
806 ASSERT(pSles->freeFront != pSles->freeRear);
807 char *buffer = pSles->freeBuffers[pSles->freeFront];
808 if (++pSles->freeFront > pSles->freeBufCount) {
809 pSles->freeFront = 0;
810 }
811
812 // put on record queue
813 SLuint32 rxRearNext = pSles->rxRear + 1;
814 if (rxRearNext > pSles->rxBufCount) {
815 rxRearNext = 0;
816 }
817 ASSERT(rxRearNext != pSles->rxFront);
818 pSles->rxBuffers[pSles->rxRear] = buffer;
819 pSles->rxRear = rxRearNext;
820 result = (*(pSles->recorderBufferQueue))->Enqueue(pSles->recorderBufferQueue,
821 buffer, pSles->bufSizeInBytes);
822 ASSERT_EQ(SL_RESULT_SUCCESS, result);
823 }
824
825 // Kick off the recorder
826 result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
827 ASSERT_EQ(SL_RESULT_SUCCESS, result);
828
829
830
831 // Tear down the objects and exit
832 status = STATUS_SUCCESS;
833 cleanup:
834
835 SLES_PRINTF("Finished initialization with status: %d", status);
836
837 }
838 return status;
839 }
840
841 // Read data from fifo2Buffer and store into pSamples.
slesProcessNext(void * pCtx,double * pSamples,long maxSamples)842 int slesProcessNext(void *pCtx, double *pSamples, long maxSamples) {
843 //int status = STATUS_FAIL;
844 sles_data *pSles = (sles_data*)pCtx;
845
846 SLES_PRINTF("slesProcessNext: pSles = %p, currentSample: %p, maxSamples = %ld",
847 pSles, pSamples, maxSamples);
848
849 int samplesRead = 0;
850
851 int currentSample = 0;
852 double *pCurrentSample = pSamples;
853 int maxValue = 32768;
854
855 if (pSles != NULL) {
856
857 SLresult result;
858 for (int i = 0; i < 10; i++) {
859 usleep(100000); // sleep for 0.1s
860 if (pSles->fifo2Buffer != NULL) {
861 for (;;) {
862 short buffer[pSles->bufSizeInFrames * pSles->channels];
863 ssize_t actual = audio_utils_fifo_read(&(pSles->fifo2), buffer,
864 pSles->bufSizeInFrames);
865 if (actual <= 0)
866 break;
867 {
868 for (int jj = 0; jj < actual && currentSample < maxSamples; jj++) {
869 *(pCurrentSample++) = ((double) buffer[jj]) / maxValue;
870 currentSample++;
871 }
872 }
873 samplesRead += actual;
874 }
875 }
876 if (pSles->injectImpulse > 0) {
877 if (pSles->injectImpulse <= 100) {
878 pSles->injectImpulse = -1;
879 write(1, "I", 1);
880 } else {
881 if ((pSles->injectImpulse % 1000) < 100) {
882 write(1, "i", 1);
883 }
884 pSles->injectImpulse -= 100;
885 }
886 } else if (i == 9) {
887 write(1, ".", 1);
888 }
889 }
890 SLBufferQueueState playerBQState;
891 result = (*(pSles->playerBufferQueue))->GetState(pSles->playerBufferQueue,
892 &playerBQState);
893 ASSERT_EQ(SL_RESULT_SUCCESS, result);
894 SLAndroidSimpleBufferQueueState recorderBQState;
895 result = (*(pSles->recorderBufferQueue))->GetState(pSles->recorderBufferQueue,
896 &recorderBQState);
897 ASSERT_EQ(SL_RESULT_SUCCESS, result);
898
899 SLES_PRINTF("End of slesProcessNext: pSles = %p, samplesRead = %d, maxSamples = %ld",
900 pSles, samplesRead, maxSamples);
901 }
902 return samplesRead;
903 }
904
905
slesDestroyServer(sles_data * pSles)906 static int slesDestroyServer(sles_data *pSles) {
907 int status = STATUS_FAIL;
908
909 SLES_PRINTF("Start slesDestroyServer: pSles = %p", pSles);
910
911 if (pSles != NULL) {
912 if (NULL != pSles->playerObject) {
913 SLES_PRINTF("stopping player...");
914 SLPlayItf playerPlay;
915 SLresult result = (*(pSles->playerObject))->GetInterface(pSles->playerObject,
916 SL_IID_PLAY, &playerPlay);
917
918 ASSERT_EQ(SL_RESULT_SUCCESS, result);
919
920 //stop player and recorder if they exist
921 result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_STOPPED);
922 ASSERT_EQ(SL_RESULT_SUCCESS, result);
923 }
924
925 if (NULL != pSles->recorderObject) {
926 SLES_PRINTF("stopping recorder...");
927 SLRecordItf recorderRecord;
928 SLresult result = (*(pSles->recorderObject))->GetInterface(pSles->recorderObject,
929 SL_IID_RECORD, &recorderRecord);
930 ASSERT_EQ(SL_RESULT_SUCCESS, result);
931
932 result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
933 ASSERT_EQ(SL_RESULT_SUCCESS, result);
934 }
935
936 usleep(1000);
937
938 audio_utils_fifo_deinit(&(pSles->fifo));
939 delete[] pSles->fifoBuffer;
940
941 SLES_PRINTF("slesDestroyServer 2");
942
943 // if (sndfile != NULL) {
944 audio_utils_fifo_deinit(&(pSles->fifo2));
945 delete[] pSles->fifo2Buffer;
946
947 SLES_PRINTF("slesDestroyServer 3");
948
949 // sf_close(sndfile);
950 // }
951 if (NULL != pSles->playerObject) {
952 (*(pSles->playerObject))->Destroy(pSles->playerObject);
953 }
954
955 SLES_PRINTF("slesDestroyServer 4");
956
957 if (NULL != pSles->recorderObject) {
958 (*(pSles->recorderObject))->Destroy(pSles->recorderObject);
959 }
960
961 SLES_PRINTF("slesDestroyServer 5");
962
963 (*(pSles->outputmixObject))->Destroy(pSles->outputmixObject);
964 SLES_PRINTF("slesDestroyServer 6");
965 (*(pSles->engineObject))->Destroy(pSles->engineObject);
966 SLES_PRINTF("slesDestroyServer 7");
967
968 //free buffers
969 if (NULL != pSles->freeBuffers) {
970 for (unsigned j = 0; j < pSles->freeBufCount; ++j) {
971 if (NULL != pSles->freeBuffers[j]) {
972 SLES_PRINTF(" free buff%d at %p",j, pSles->freeBuffers[j]);
973 free (pSles->freeBuffers[j]);
974 }
975 }
976 SLES_PRINTF(" free freeBuffers at %p", pSles->freeBuffers);
977 free(pSles->freeBuffers);
978 } else {
979 SLES_PRINTF(" freeBuffers NULL, no need to free");
980 }
981
982
983 if (NULL != pSles->rxBuffers) {
984 SLES_PRINTF(" free rxBuffers at %p", pSles->rxBuffers);
985 free(pSles->rxBuffers);
986 } else {
987 SLES_PRINTF(" rxBuffers NULL, no need to free");
988 }
989
990 if (NULL != pSles->txBuffers) {
991 SLES_PRINTF(" free txBuffers at %p", pSles->txBuffers);
992 free(pSles->txBuffers);
993 } else {
994 SLES_PRINTF(" txBuffers NULL, no need to free");
995 }
996
997
998 status = STATUS_SUCCESS;
999 }
1000 SLES_PRINTF("End slesDestroyServer: status = %d", status);
1001 return status;
1002 }
1003
1004
slesGetRecorderBufferPeriod(void * pCtx)1005 int* slesGetRecorderBufferPeriod(void *pCtx) {
1006 sles_data *pSles = (sles_data*)pCtx;
1007 return pSles->recorderBufferStats.buffer_period;
1008 }
1009
slesGetRecorderMaxBufferPeriod(void * pCtx)1010 int slesGetRecorderMaxBufferPeriod(void *pCtx) {
1011 sles_data *pSles = (sles_data*)pCtx;
1012 return pSles->recorderBufferStats.max_buffer_period;
1013 }
1014
slesGetRecorderVarianceBufferPeriod(void * pCtx)1015 int64_t slesGetRecorderVarianceBufferPeriod(void *pCtx) {
1016 sles_data *pSles = (sles_data*)pCtx;
1017 return pSles->recorderBufferStats.var;
1018 }
1019
slesGetPlayerBufferPeriod(void * pCtx)1020 int* slesGetPlayerBufferPeriod(void *pCtx) {
1021 sles_data *pSles = (sles_data*)pCtx;
1022 return pSles->playerBufferStats.buffer_period;
1023 }
1024
slesGetPlayerMaxBufferPeriod(void * pCtx)1025 int slesGetPlayerMaxBufferPeriod(void *pCtx) {
1026 sles_data *pSles = (sles_data*)pCtx;
1027 return pSles->playerBufferStats.max_buffer_period;
1028 }
1029
slesGetPlayerVarianceBufferPeriod(void * pCtx)1030 int64_t slesGetPlayerVarianceBufferPeriod(void *pCtx) {
1031 sles_data *pSles = (sles_data*)pCtx;
1032 return pSles->playerBufferStats.var;
1033 }
1034
slesGetCaptureRank(void * pCtx)1035 int slesGetCaptureRank(void *pCtx) {
1036 sles_data *pSles = (sles_data*)pCtx;
1037 // clear the capture flags since they're being handled now
1038 int recorderRank = android_atomic_exchange(0, &pSles->recorderBufferStats.captureRank);
1039 int playerRank = android_atomic_exchange(0, &pSles->playerBufferStats.captureRank);
1040
1041 if (recorderRank > playerRank) {
1042 return recorderRank;
1043 } else {
1044 return playerRank;
1045 }
1046 }
1047
slesGetPlayerTimeStampsAndExpectedBufferPeriod(void * pCtx,callbackTimeStamps ** ppTSs)1048 int slesGetPlayerTimeStampsAndExpectedBufferPeriod(void *pCtx, callbackTimeStamps **ppTSs) {
1049 sles_data *pSles = (sles_data*)pCtx;
1050 *ppTSs = &pSles->playerTimeStamps;
1051 return pSles->expectedBufferPeriod;
1052 }
1053
slesGetRecorderTimeStampsAndExpectedBufferPeriod(void * pCtx,callbackTimeStamps ** ppTSs)1054 int slesGetRecorderTimeStampsAndExpectedBufferPeriod(void *pCtx, callbackTimeStamps **ppTSs) {
1055 sles_data *pSles = (sles_data*)pCtx;
1056 *ppTSs = &pSles->recorderTimeStamps;
1057 return pSles->expectedBufferPeriod;
1058 }
1059