1f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten/* 2f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * Copyright (C) 2010 The Android Open Source Project 3f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * 4f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * Licensed under the Apache License, Version 2.0 (the "License"); 5f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * you may not use this file except in compliance with the License. 6f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * You may obtain a copy of the License at 7f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * 8f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * http://www.apache.org/licenses/LICENSE-2.0 9f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * 10f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * Unless required by applicable law or agreed to in writing, software 11f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * distributed under the License is distributed on an "AS IS" BASIS, 12f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * See the License for the specific language governing permissions and 14f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten * limitations under the License. 15f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten */ 16f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 177126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Test program to record from default audio input and playback to default audio output. 187126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// It will generate feedback (Larsen effect) if played through on-device speakers, 197126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// or acts as a delay if played through headset. 205ef8af762b27b4fa45f59d944b11ad00311cb14bGlenn Kasten 21c6853892c94800e72c0bd676d5d2136d48cea76eGlenn Kasten#include <SLES/OpenSLES.h> 22c6853892c94800e72c0bd676d5d2136d48cea76eGlenn Kasten#include <SLES/OpenSLES_Android.h> 23f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten#include <assert.h> 247126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#include <pthread.h> 25f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten#include <stdio.h> 26f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten#include <stdlib.h> 27527b7d2e606abdbde0e29fe75f7e9a67285629d2Glenn Kasten#include <string.h> 28f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten#include <unistd.h> 29f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 306e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten#include <audio_utils/fifo.h> 3185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten#include <audio_utils/sndfile.h> 32fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 330ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten#define ASSERT_EQ(x, y) do { if ((x) == (y)) ; else { fprintf(stderr, "0x%x != 0x%x\n", \ 340ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten (unsigned) (x), (unsigned) (y)); assert((x) == (y)); } } while (0) 35f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 360ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten// default values 3758432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kastenstatic SLuint32 rxBufCount = 2; // -r# 380ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kastenstatic SLuint32 txBufCount = 2; // -t# 39fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kastenstatic SLuint32 bufSizeInFrames = 240; // -f# 4058432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kastenstatic SLuint32 channels = 1; // -c# 41fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kastenstatic SLuint32 sampleRate = 48000; // -s# 427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 exitAfterSeconds = 60; // -e# 437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 freeBufCount = 0; // calculated 44e5d006b298ce7683d66f7ec86136403cf5fb20d6Glenn Kastenstatic SLuint32 bufSizeInBytes = 0; // calculated 450ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten 467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Storage area for the buffer queues 477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic char **rxBuffers; 487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic char **txBuffers; 497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic char **freeBuffers; 50f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 517126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Buffer indices 527126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 rxFront; // oldest recording 537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 rxRear; // next to be recorded 547126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 txFront; // oldest playing 557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 txRear; // next to be played 567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 freeFront; // oldest free 577126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 freeRear; // next to be freed 58f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 597126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLAndroidSimpleBufferQueueItf recorderBufferQueue; 607126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLBufferQueueItf playerBufferQueue; 61f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 627126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 630ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten 64add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kastenstatic audio_utils_fifo *fifo; 6513e4fe3294ea101738253c72a3382101918d5682Glenn Kastenstatic audio_utils_fifo_reader *fifoReader; 6613e4fe3294ea101738253c72a3382101918d5682Glenn Kastenstatic audio_utils_fifo_writer *fifoWriter; 67fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 68add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kastenstatic audio_utils_fifo *fifo2; 696e55f367758180f2f76694f9dcfbbab269a25162Glenn Kastenstatic short *fifo2Buffer = NULL; 7013e4fe3294ea101738253c72a3382101918d5682Glenn Kastenstatic audio_utils_fifo_reader *fifo2Reader; 7113e4fe3294ea101738253c72a3382101918d5682Glenn Kastenstatic audio_utils_fifo_writer *fifo2Writer; 7285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten 7385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kastenstatic int injectImpulse; 7485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten 757126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Called after audio recorder fills a buffer with data 76086a6f51a7b12880ed114962136972f89ed70da2Glenn Kastenstatic void recorderCallback(SLAndroidSimpleBufferQueueItf caller __unused, void *context __unused) 770ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten{ 787126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten SLresult result; 797126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 807126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten pthread_mutex_lock(&mutex); 817126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 827126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // We should only be called when a recording buffer is done 837126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(rxFront <= rxBufCount); 847126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(rxRear <= rxBufCount); 857126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(rxFront != rxRear); 867126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten char *buffer = rxBuffers[rxFront]; 877126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 887126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Remove buffer from record queue 897126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (++rxFront > rxBufCount) { 907126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxFront = 0; 917126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 927126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 93fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#if 1 9413e4fe3294ea101738253c72a3382101918d5682Glenn Kasten ssize_t actual = fifoWriter->write(buffer, (size_t) bufSizeInFrames); 95fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten if (actual != (ssize_t) bufSizeInFrames) { 96fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten write(1, "?", 1); 97fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten } 98fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 9985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // This is called by a realtime (SCHED_FIFO) thread, 10085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // and it is unsafe to do I/O as it could block for unbounded time. 10185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // Flash filesystem is especially notorious for blocking. 1026e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten if (fifo2Buffer != NULL) { 10313e4fe3294ea101738253c72a3382101918d5682Glenn Kasten actual = fifo2Writer->write(buffer, (size_t) bufSizeInFrames); 10485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if (actual != (ssize_t) bufSizeInFrames) { 10585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten write(1, "?", 1); 10685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 10785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 10885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten 109fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // Enqueue this same buffer for the recorder to fill again. 110fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes); 111fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 112fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 113fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // Update our model of the record queue 114fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten SLuint32 rxRearNext = rxRear+1; 115fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten if (rxRearNext > rxBufCount) { 116fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten rxRearNext = 0; 117fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten } 118fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten assert(rxRearNext != rxFront); 119fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten rxBuffers[rxRear] = buffer; 120fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten rxRear = rxRearNext; 121fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 122fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#else 1237126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Enqueue the just-filled buffer for the player 1247126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, bufSizeInBytes); 1257126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (SL_RESULT_SUCCESS == result) { 1267126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1277126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // There was room in the play queue, update our model of it 1287126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(txFront <= txBufCount); 1297126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(txRear <= txBufCount); 1307126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten SLuint32 txRearNext = txRear+1; 1317126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (txRearNext > txBufCount) { 1327126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten txRearNext = 0; 1337126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 1347126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(txRearNext != txFront); 1357126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten txBuffers[txRear] = buffer; 1367126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten txRear = txRearNext; 1377126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } else { 1397126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1407126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Here if record has a filled buffer to play, but play queue is full. 1417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(SL_RESULT_BUFFER_INSUFFICIENT == result); 1427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten write(1, "?", 1); 1437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // We could either try again later, or discard. For now we discard and re-use buffer. 1457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Enqueue this same buffer for the recorder to fill again. 1467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes); 1477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 1487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Update our model of the record queue 1507126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten SLuint32 rxRearNext = rxRear+1; 1517126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (rxRearNext > rxBufCount) { 1527126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxRearNext = 0; 1537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 1547126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(rxRearNext != rxFront); 1557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxBuffers[rxRear] = buffer; 1567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxRear = rxRearNext; 1577126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1587126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 159fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#endif 1607126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1617126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten pthread_mutex_unlock(&mutex); 1620ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten} 1630ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten 1647126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1657126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Called after audio player empties a buffer of data 166086a6f51a7b12880ed114962136972f89ed70da2Glenn Kastenstatic void playerCallback(SLBufferQueueItf caller __unused, void *context __unused) 167f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten{ 168f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLresult result; 1697126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1707126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten pthread_mutex_lock(&mutex); 1717126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 1727126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Get the buffer that just finished playing 1737126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(txFront <= txBufCount); 1747126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(txRear <= txBufCount); 1757126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(txFront != txRear); 1767126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten char *buffer = txBuffers[txFront]; 1777126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (++txFront > txBufCount) { 1787126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten txFront = 0; 179e5d006b298ce7683d66f7ec86136403cf5fb20d6Glenn Kasten } 180f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 181fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#if 1 18213e4fe3294ea101738253c72a3382101918d5682Glenn Kasten ssize_t actual = fifoReader->read(buffer, bufSizeInFrames); 183fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten if (actual != (ssize_t) bufSizeInFrames) { 184fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten write(1, "/", 1); 185fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // on underrun from pipe, substitute silence 186fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten memset(buffer, 0, bufSizeInFrames * channels * sizeof(short)); 187fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten } 188fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 18985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if (injectImpulse == -1) { 19085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // Experimentally, a single frame impulse was insufficient to trigger feedback. 19185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // Also a Nyquist frequency signal was also insufficient, probably because 19285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // the response of output and/or input path was not adequate at high frequencies. 19385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // This short burst of a few cycles of square wave at Nyquist/4 was found to work well. 194086a6f51a7b12880ed114962136972f89ed70da2Glenn Kasten for (unsigned i = 0; i < bufSizeInFrames / 8; i += 8) { 19585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten for (int j = 0; j < 8; j++) { 196086a6f51a7b12880ed114962136972f89ed70da2Glenn Kasten for (unsigned k = 0; k < channels; k++) { 19785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten ((short *)buffer)[(i+j)*channels+k] = j < 4 ? 0x7FFF : 0x8000; 19885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 19985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 20085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 20185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten injectImpulse = 0; 20285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 20385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten 204fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // Enqueue the filled buffer for playing 205fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, bufSizeInBytes); 206fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 207fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 208fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // Update our model of the player queue 209fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten assert(txFront <= txBufCount); 210fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten assert(txRear <= txBufCount); 211fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten SLuint32 txRearNext = txRear+1; 212fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten if (txRearNext > txBufCount) { 213fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten txRearNext = 0; 214fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten } 215fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten assert(txRearNext != txFront); 216fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten txBuffers[txRear] = buffer; 217fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten txRear = txRearNext; 218fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 219fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#else 2207126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // First try to enqueue the free buffer for recording 2210ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes); 2225ef8af762b27b4fa45f59d944b11ad00311cb14bGlenn Kasten if (SL_RESULT_SUCCESS == result) { 2237126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 2247126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // There was room in the record queue, update our model of it 2257126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(rxFront <= rxBufCount); 2267126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(rxRear <= rxBufCount); 2277126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten SLuint32 rxRearNext = rxRear+1; 2287126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (rxRearNext > rxBufCount) { 2297126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxRearNext = 0; 2307126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 2317126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(rxRearNext != rxFront); 2327126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxBuffers[rxRear] = buffer; 2337126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxRear = rxRearNext; 2347126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 2350ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } else { 2367126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 2377126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Here if record queue is full 2387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(SL_RESULT_BUFFER_INSUFFICIENT == result); 2397126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 2407126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Instead enqueue the free buffer on the free queue 2417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(freeFront <= freeBufCount); 2427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(freeRear <= freeBufCount); 2437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten SLuint32 freeRearNext = freeRear+1; 2447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (freeRearNext > freeBufCount) { 2457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeRearNext = 0; 2467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 2477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // There must always be room in the free queue 2487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(freeRearNext != freeFront); 2497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeBuffers[freeRear] = buffer; 2507126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeRear = freeRearNext; 2517126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 2525ef8af762b27b4fa45f59d944b11ad00311cb14bGlenn Kasten } 253fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#endif 254f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 2557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten pthread_mutex_unlock(&mutex); 256f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten} 257f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 2587126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Main program 259f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kastenint main(int argc, char **argv) 260f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten{ 26185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten const char *outFileName = NULL; 2620ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten // process command-line options 2630ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten int i; 2640ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten for (i = 1; i < argc; ++i) { 2650ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten char *arg = argv[i]; 2667126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (arg[0] != '-') { 2670ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten break; 2687126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 2697126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // -r# number of slots in receive buffer queue 2700ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten if (!strncmp(arg, "-r", 2)) { 2710ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten rxBufCount = atoi(&arg[2]); 2727126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (rxBufCount < 1 || rxBufCount > 16) { 2737126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten fprintf(stderr, "%s: unusual receive buffer queue size (%u buffers)\n", argv[0], 2740ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten (unsigned) rxBufCount); 2757126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 2767126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // -t# number of slots in transmit buffer queue 2770ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } else if (!strncmp(arg, "-t", 2)) { 2780ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten txBufCount = atoi(&arg[2]); 2797126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (txBufCount < 1 || txBufCount > 16) { 2807126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten fprintf(stderr, "%s: unusual transmit buffer queue size (%u buffers)\n", argv[0], 2810ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten (unsigned) txBufCount); 2827126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 2830ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten // -f# size of each buffer in frames 2840ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } else if (!strncmp(arg, "-f", 2)) { 2850ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten bufSizeInFrames = atoi(&arg[2]); 2860ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten if (bufSizeInFrames == 0) { 2870ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, "%s: unusual buffer size (%u frames)\n", argv[0], 2880ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten (unsigned) bufSizeInFrames); 2890ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } 2900ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten // -c1 mono or -c2 stereo 2910ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } else if (!strncmp(arg, "-c", 2)) { 2920ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten channels = atoi(&arg[2]); 2930ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten if (channels < 1 || channels > 2) { 2940ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, "%s: unusual channel count ignored (%u)\n", argv[0], 2950ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten (unsigned) channels); 2960ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten channels = 2; 2970ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } 2980ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten // -s# sample rate in Hz 2990ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } else if (!strncmp(arg, "-s", 2)) { 3000ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten sampleRate = atoi(&arg[2]); 3010ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten switch (sampleRate) { 3020ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten case 8000: 3030ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten case 11025: 3047126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten case 12000: 3050ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten case 16000: 3060ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten case 22050: 3077126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten case 24000: 3080ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten case 32000: 3090ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten case 44100: 3107126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten case 48000: 3110ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten break; 3120ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten default: 3130ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, "%s: unusual sample rate (%u Hz)\n", argv[0], 3140ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten (unsigned) sampleRate); 3150ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten break; 3160ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } 3177126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // -e# exit after this many seconds 3187126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } else if (!strncmp(arg, "-e", 2)) { 3197126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten exitAfterSeconds = atoi(&arg[2]); 32085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // -ofile log to output file also 32185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } else if (!strncmp(arg, "-o", 2)) { 32285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten outFileName = &arg[2]; 32385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // -i# inject an impulse after # milliseconds 32485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } else if (!strncmp(arg, "-i", 2)) { 32585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten injectImpulse = atoi(&arg[2]); 3260ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } else 3270ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, "%s: unknown option %s\n", argv[0], arg); 3280ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } 3297126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // no other arguments allowed 3300ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten if (i < argc) { 33185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten fprintf(stderr, "usage: %s -r# -t# -f# -s# -c# -i# -ofile\n", argv[0]); 3320ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, " -r# receive buffer queue count for microphone input, default 1\n"); 3330ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, " -t# transmit buffer queue count for speaker output, default 2\n"); 3340ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, " -f# number of frames per buffer, default 512\n"); 3350ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, " -s# sample rate in Hz, default 44100\n"); 3360ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, " -c1 mono\n"); 3370ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten fprintf(stderr, " -c2 stereo, default\n"); 33885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten fprintf(stderr, " -i# inject impulse after # milliseconds\n"); 33985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten fprintf(stderr, " -ofile log input to specified .wav file also\n"); 3400ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten } 34185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten 3427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // compute total free buffers as -r plus -t 3437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeBufCount = rxBufCount + txBufCount; 3447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // compute buffer size 3450ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten bufSizeInBytes = channels * bufSizeInFrames * sizeof(short); 3467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 3477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Initialize free buffers 3487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeBuffers = (char **) calloc(freeBufCount+1, sizeof(char *)); 3497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten unsigned j; 3507126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten for (j = 0; j < freeBufCount; ++j) { 3517126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeBuffers[j] = (char *) malloc(bufSizeInBytes); 3527126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 3537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeFront = 0; 3547126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeRear = freeBufCount; 3557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeBuffers[j] = NULL; 3567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 3577126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Initialize record queue 3587126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxBuffers = (char **) calloc(rxBufCount+1, sizeof(char *)); 3597126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxFront = 0; 3607126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxRear = 0; 3617126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 3627126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Initialize play queue 3637126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten txBuffers = (char **) calloc(txBufCount+1, sizeof(char *)); 3647126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten txFront = 0; 3657126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten txRear = 0; 3667126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 3676e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten size_t frameSize = channels * sizeof(short); 3686e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten#define FIFO_FRAMES 1024 3696e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten short *fifoBuffer = new short[FIFO_FRAMES * channels]; 370add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten fifo = new audio_utils_fifo(FIFO_FRAMES, frameSize, fifoBuffer); 37113e4fe3294ea101738253c72a3382101918d5682Glenn Kasten fifoReader = new audio_utils_fifo_reader(*fifo, true /*throttlesWriter*/); 37213e4fe3294ea101738253c72a3382101918d5682Glenn Kasten fifoWriter = new audio_utils_fifo_writer(*fifo); 373fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 37485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten SNDFILE *sndfile; 37585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if (outFileName != NULL) { 37685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten // create .wav writer 37785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten SF_INFO info; 37885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten info.frames = 0; 37985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten info.samplerate = sampleRate; 38085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten info.channels = channels; 38185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; 38285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten sndfile = sf_open(outFileName, SFM_WRITE, &info); 38385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if (sndfile != NULL) { 3846e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten#define FIFO2_FRAMES 65536 3856e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten fifo2Buffer = new short[FIFO2_FRAMES * channels]; 386add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten fifo2 = new audio_utils_fifo(FIFO2_FRAMES, frameSize, fifo2Buffer); 38713e4fe3294ea101738253c72a3382101918d5682Glenn Kasten fifo2Reader = new audio_utils_fifo_reader(*fifo2, true /*throttlesWriter*/); 38813e4fe3294ea101738253c72a3382101918d5682Glenn Kasten fifo2Writer = new audio_utils_fifo_writer(*fifo2); 38985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } else { 39085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten fprintf(stderr, "sf_open failed\n"); 39185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 39285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } else { 39385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten sndfile = NULL; 39485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 39585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten 396f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLresult result; 397f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 398f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten // create engine 399f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLObjectItf engineObject; 400f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); 401f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 402f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); 403f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 404f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLEngineItf engineEngine; 405f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); 406f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 407f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 408f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten // create output mix 409f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLObjectItf outputmixObject; 410f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*engineEngine)->CreateOutputMix(engineEngine, &outputmixObject, 0, NULL, NULL); 411f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 412f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*outputmixObject)->Realize(outputmixObject, SL_BOOLEAN_FALSE); 413f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 414f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 415f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten // create an audio player with buffer queue source and output mix sink 416f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLDataSource audiosrc; 417f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLDataSink audiosnk; 418f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLDataFormat_PCM pcm; 419f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLDataLocator_OutputMix locator_outputmix; 42001e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten SLDataLocator_BufferQueue locator_bufferqueue_tx; 42101e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten locator_bufferqueue_tx.locatorType = SL_DATALOCATOR_BUFFERQUEUE; 42201e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten locator_bufferqueue_tx.numBuffers = txBufCount; 423f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 424f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten locator_outputmix.outputMix = outputmixObject; 425f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten pcm.formatType = SL_DATAFORMAT_PCM; 4260ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten pcm.numChannels = channels; 4270ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten pcm.samplesPerSec = sampleRate * 1000; 428f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; 429f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten pcm.containerSize = 16; 4300ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten pcm.channelMask = channels == 1 ? SL_SPEAKER_FRONT_CENTER : 4310ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); 432f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; 43301e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten audiosrc.pLocator = &locator_bufferqueue_tx; 434f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten audiosrc.pFormat = &pcm; 435f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten audiosnk.pLocator = &locator_outputmix; 436f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten audiosnk.pFormat = NULL; 4377126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten SLObjectItf playerObject = NULL; 4387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten SLObjectItf recorderObject = NULL; 43901e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten SLInterfaceID ids_tx[1] = {SL_IID_BUFFERQUEUE}; 44001e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten SLboolean flags_tx[1] = {SL_BOOLEAN_TRUE}; 4410ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audiosrc, &audiosnk, 44201e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten 1, ids_tx, flags_tx); 4437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (SL_RESULT_CONTENT_UNSUPPORTED == result) { 44458432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kasten fprintf(stderr, "Could not create audio player (result %x), check sample rate\n", result); 4457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten goto cleanup; 4467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 447f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 448f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE); 449f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 450f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLPlayItf playerPlay; 451f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay); 452f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 453f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &playerBufferQueue); 454f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 4557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, playerCallback, NULL); 4567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 457fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 458fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // Enqueue some zero buffers for the player 459fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten for (j = 0; j < txBufCount; ++j) { 460fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 461fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // allocate a free buffer 462fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten assert(freeFront != freeRear); 463fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten char *buffer = freeBuffers[freeFront]; 464fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten if (++freeFront > freeBufCount) { 465fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten freeFront = 0; 466fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten } 467fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 468fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // put on play queue 469fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten SLuint32 txRearNext = txRear + 1; 470fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten if (txRearNext > txBufCount) { 471fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten txRearNext = 0; 472fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten } 473fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten assert(txRearNext != txFront); 474fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten txBuffers[txRear] = buffer; 475fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten txRear = txRearNext; 476fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten result = (*playerBufferQueue)->Enqueue(playerBufferQueue, 477fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten buffer, bufSizeInBytes); 478fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 479fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten } 480fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 481f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING); 482f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 483f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 484f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten // Create an audio recorder with microphone device source and buffer queue sink. 485f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten // The buffer queue as sink is an Android-specific extension. 486f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 487f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLDataLocator_IODevice locator_iodevice; 48801e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten SLDataLocator_AndroidSimpleBufferQueue locator_bufferqueue_rx; 489f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten locator_iodevice.locatorType = SL_DATALOCATOR_IODEVICE; 490f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten locator_iodevice.deviceType = SL_IODEVICE_AUDIOINPUT; 491f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten locator_iodevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; 492f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten locator_iodevice.device = NULL; 493f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten audiosrc.pLocator = &locator_iodevice; 49435c82a8173d1fcc18ae0bc08954a018bfce041deGlenn Kasten audiosrc.pFormat = NULL; 49501e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten locator_bufferqueue_rx.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; 49601e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten locator_bufferqueue_rx.numBuffers = rxBufCount; 49701e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten audiosnk.pLocator = &locator_bufferqueue_rx; 498f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten audiosnk.pFormat = &pcm; 4997126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten { 50001e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten SLInterfaceID ids_rx[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; 50101e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten SLboolean flags_rx[1] = {SL_BOOLEAN_TRUE}; 5020ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audiosrc, 50301e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten &audiosnk, 1, ids_rx, flags_rx); 5047126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (SL_RESULT_SUCCESS != result) { 50558432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kasten fprintf(stderr, "Could not create audio recorder (result %x), " 5067126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten "check sample rate and channel count\n", result); 5077126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten goto cleanup; 5087126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 5097126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 510f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 511f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE); 512f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 513f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLRecordItf recorderRecord; 514f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord); 515f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 51601e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, 5170ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten &recorderBufferQueue); 518f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 519f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, recorderCallback, NULL); 520f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 521f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 522f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten // Enqueue some empty buffers for the recorder 5237126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten for (j = 0; j < rxBufCount; ++j) { 5247126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 5257126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // allocate a free buffer 5267126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(freeFront != freeRear); 5277126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten char *buffer = freeBuffers[freeFront]; 5287126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (++freeFront > freeBufCount) { 5297126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten freeFront = 0; 5307126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 5317126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 5327126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // put on record queue 5337126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten SLuint32 rxRearNext = rxRear + 1; 5347126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (rxRearNext > rxBufCount) { 5357126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxRearNext = 0; 5367126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 5377126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten assert(rxRearNext != rxFront); 5387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxBuffers[rxRear] = buffer; 5397126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten rxRear = rxRearNext; 5400ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, 5417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten buffer, bufSizeInBytes); 542f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 543f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten } 544f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 545f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten // Kick off the recorder 546f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING); 547f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 548f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 549fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#if 0 550fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten // give recorder a head start so that the pipe is initially filled 551fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten sleep(1); 552fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#endif 553fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten 554f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten // Wait patiently 5557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten do { 55685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten for (int i = 0; i < 10; i++) { 55785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten usleep(100000); 5586e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten if (fifo2Buffer != NULL) { 55985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten for (;;) { 56085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten short buffer[bufSizeInFrames * channels]; 56113e4fe3294ea101738253c72a3382101918d5682Glenn Kasten ssize_t actual = fifo2Reader->read(buffer, bufSizeInFrames); 56285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if (actual <= 0) 56385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten break; 56485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten (void) sf_writef_short(sndfile, buffer, (sf_count_t) actual); 56585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 56685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 56785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if (injectImpulse > 0) { 56885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if (injectImpulse <= 100) { 56985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten injectImpulse = -1; 57085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten write(1, "I", 1); 57185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } else { 57285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if ((injectImpulse % 1000) < 100) { 57385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten write(1, "i", 1); 57485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 57585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten injectImpulse -= 100; 57685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 57785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } else if (i == 9) { 57885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten write(1, ".", 1); 57985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 58085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 581f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten SLBufferQueueState playerBQState; 582f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*playerBufferQueue)->GetState(playerBufferQueue, &playerBQState); 583f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 58401e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten SLAndroidSimpleBufferQueueState recorderBQState; 585f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten result = (*recorderBufferQueue)->GetState(recorderBufferQueue, &recorderBQState); 586f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten ASSERT_EQ(SL_RESULT_SUCCESS, result); 5877126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } while (--exitAfterSeconds); 5887126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten 5897126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten // Tear down the objects and exit 5907126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastencleanup: 59113e4fe3294ea101738253c72a3382101918d5682Glenn Kasten delete fifoWriter; 59213e4fe3294ea101738253c72a3382101918d5682Glenn Kasten fifoWriter = NULL; 59313e4fe3294ea101738253c72a3382101918d5682Glenn Kasten delete fifoReader; 59413e4fe3294ea101738253c72a3382101918d5682Glenn Kasten fifoReader = NULL; 595add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten delete fifo; 596add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten fifo = NULL; 5976e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten delete[] fifoBuffer; 598add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten fifoBuffer = NULL; 5996e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten 60085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten if (sndfile != NULL) { 60113e4fe3294ea101738253c72a3382101918d5682Glenn Kasten delete fifo2Writer; 60213e4fe3294ea101738253c72a3382101918d5682Glenn Kasten fifo2Writer = NULL; 60313e4fe3294ea101738253c72a3382101918d5682Glenn Kasten delete fifo2Reader; 60413e4fe3294ea101738253c72a3382101918d5682Glenn Kasten fifo2Reader = NULL; 605add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten delete fifo2; 606add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten fifo2 = NULL; 6076e55f367758180f2f76694f9dcfbbab269a25162Glenn Kasten delete[] fifo2Buffer; 608add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten fifo2Buffer = NULL; 60985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten sf_close(sndfile); 610add2a2112de08f840459de78e8f8eb16d8c888c3Glenn Kasten sndfile = NULL; 61185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten } 6127126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (NULL != playerObject) { 6137126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten (*playerObject)->Destroy(playerObject); 6147126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten } 6157126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten if (NULL != recorderObject) { 6167126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten (*recorderObject)->Destroy(recorderObject); 617f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten } 6187126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten (*outputmixObject)->Destroy(outputmixObject); 6197126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten (*engineObject)->Destroy(engineObject); 620f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten 6217126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten return EXIT_SUCCESS; 622f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten} 623