slesTestFeedback.cpp revision 85079ea0cc7592029762b7c44a6def84c43bebb0
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
3085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten#include <audio_utils/sndfile.h>
31fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#include <media/nbaio/MonoPipe.h>
32fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#include <media/nbaio/MonoPipeReader.h>
33fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
340ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten#define ASSERT_EQ(x, y) do { if ((x) == (y)) ; else { fprintf(stderr, "0x%x != 0x%x\n", \
350ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    (unsigned) (x), (unsigned) (y)); assert((x) == (y)); } } while (0)
36f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
370ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten// default values
3858432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kastenstatic SLuint32 rxBufCount = 2;     // -r#
390ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kastenstatic SLuint32 txBufCount = 2;     // -t#
40fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kastenstatic SLuint32 bufSizeInFrames = 240;  // -f#
4158432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kastenstatic SLuint32 channels = 1;       // -c#
42fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kastenstatic SLuint32 sampleRate = 48000; // -s#
437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 exitAfterSeconds = 60; // -e#
447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 freeBufCount = 0;   // calculated
45e5d006b298ce7683d66f7ec86136403cf5fb20d6Glenn Kastenstatic SLuint32 bufSizeInBytes = 0; // calculated
460ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten
477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Storage area for the buffer queues
487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic char **rxBuffers;
497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic char **txBuffers;
507126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic char **freeBuffers;
51f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
527126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Buffer indices
537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 rxFront;    // oldest recording
547126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 rxRear;     // next to be recorded
557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 txFront;    // oldest playing
567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 txRear;     // next to be played
577126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 freeFront;  // oldest free
587126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLuint32 freeRear;   // next to be freed
59f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
607126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLAndroidSimpleBufferQueueItf recorderBufferQueue;
617126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic SLBufferQueueItf playerBufferQueue;
62f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
637126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
640ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten
65fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kastenstatic android::MonoPipeReader *pipeReader;
66fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kastenstatic android::MonoPipe *pipeWriter;
67fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
6885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kastenstatic android::MonoPipeReader *pipeReader2;
6985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kastenstatic android::MonoPipe *pipeWriter2;
7085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten
7185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kastenstatic int injectImpulse;
7285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten
737126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Called after audio recorder fills a buffer with data
747126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic void recorderCallback(SLAndroidSimpleBufferQueueItf caller, void *context)
750ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten{
767126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    SLresult result;
777126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
787126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    pthread_mutex_lock(&mutex);
797126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
807126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // We should only be called when a recording buffer is done
817126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    assert(rxFront <= rxBufCount);
827126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    assert(rxRear <= rxBufCount);
837126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    assert(rxFront != rxRear);
847126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    char *buffer = rxBuffers[rxFront];
857126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
867126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // Remove buffer from record queue
877126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    if (++rxFront > rxBufCount) {
887126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        rxFront = 0;
897126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    }
907126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
91fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#if 1
92fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    ssize_t actual = pipeWriter->write(buffer, (size_t) bufSizeInFrames);
93fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    if (actual != (ssize_t) bufSizeInFrames) {
94fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        write(1, "?", 1);
95fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    }
96fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
9785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    // This is called by a realtime (SCHED_FIFO) thread,
9885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    // and it is unsafe to do I/O as it could block for unbounded time.
9985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    // Flash filesystem is especially notorious for blocking.
10085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    if (pipeWriter2 != NULL) {
10185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        actual = pipeWriter2->write(buffer, (size_t) bufSizeInFrames);
10285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        if (actual != (ssize_t) bufSizeInFrames) {
10385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            write(1, "?", 1);
10485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        }
10585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    }
10685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten
107fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    // Enqueue this same buffer for the recorder to fill again.
108fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes);
109fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
110fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
111fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    // Update our model of the record queue
112fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    SLuint32 rxRearNext = rxRear+1;
113fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    if (rxRearNext > rxBufCount) {
114fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        rxRearNext = 0;
115fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    }
116fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    assert(rxRearNext != rxFront);
117fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    rxBuffers[rxRear] = buffer;
118fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    rxRear = rxRearNext;
119fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
120fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#else
1217126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // Enqueue the just-filled buffer for the player
1227126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, bufSizeInBytes);
1237126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    if (SL_RESULT_SUCCESS == result) {
1247126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1257126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // There was room in the play queue, update our model of it
1267126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(txFront <= txBufCount);
1277126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(txRear <= txBufCount);
1287126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        SLuint32 txRearNext = txRear+1;
1297126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        if (txRearNext > txBufCount) {
1307126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            txRearNext = 0;
1317126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        }
1327126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(txRearNext != txFront);
1337126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        txBuffers[txRear] = buffer;
1347126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        txRear = txRearNext;
1357126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1367126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    } else {
1377126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // Here if record has a filled buffer to play, but play queue is full.
1397126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(SL_RESULT_BUFFER_INSUFFICIENT == result);
1407126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        write(1, "?", 1);
1417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // We could either try again later, or discard. For now we discard and re-use buffer.
1437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // Enqueue this same buffer for the recorder to fill again.
1447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes);
1457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        ASSERT_EQ(SL_RESULT_SUCCESS, result);
1467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // Update our model of the record queue
1487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        SLuint32 rxRearNext = rxRear+1;
1497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        if (rxRearNext > rxBufCount) {
1507126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            rxRearNext = 0;
1517126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        }
1527126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(rxRearNext != rxFront);
1537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        rxBuffers[rxRear] = buffer;
1547126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        rxRear = rxRearNext;
1557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    }
157fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#endif
1587126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1597126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    pthread_mutex_unlock(&mutex);
1600ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten}
1610ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten
1627126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1637126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Called after audio player empties a buffer of data
1647126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenstatic void playerCallback(SLBufferQueueItf caller, void *context)
165f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten{
166f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLresult result;
1677126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1687126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    pthread_mutex_lock(&mutex);
1697126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1707126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // Get the buffer that just finished playing
1717126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    assert(txFront <= txBufCount);
1727126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    assert(txRear <= txBufCount);
1737126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    assert(txFront != txRear);
1747126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    char *buffer = txBuffers[txFront];
1757126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    if (++txFront > txBufCount) {
1767126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        txFront = 0;
177e5d006b298ce7683d66f7ec86136403cf5fb20d6Glenn Kasten    }
178f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
179fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#if 1
180fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    ssize_t actual = pipeReader->read(buffer, bufSizeInFrames, (int64_t) -1);
181fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    if (actual != (ssize_t) bufSizeInFrames) {
182fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        write(1, "/", 1);
183fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        // on underrun from pipe, substitute silence
184fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        memset(buffer, 0, bufSizeInFrames * channels * sizeof(short));
185fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    }
186fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
18785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    if (injectImpulse == -1) {
18885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        // Experimentally, a single frame impulse was insufficient to trigger feedback.
18985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        // Also a Nyquist frequency signal was also insufficient, probably because
19085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        // the response of output and/or input path was not adequate at high frequencies.
19185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        // This short burst of a few cycles of square wave at Nyquist/4 was found to work well.
19285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        for (int i = 0; i < bufSizeInFrames / 8; i += 8) {
19385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            for (int j = 0; j < 8; j++) {
19485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                for (int k = 0; k < channels; k++) {
19585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    ((short *)buffer)[(i+j)*channels+k] = j < 4 ? 0x7FFF : 0x8000;
19685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                }
19785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            }
19885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        }
19985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        injectImpulse = 0;
20085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    }
20185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten
202fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    // Enqueue the filled buffer for playing
203fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, bufSizeInBytes);
204fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
205fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
206fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    // Update our model of the player queue
207fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    assert(txFront <= txBufCount);
208fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    assert(txRear <= txBufCount);
209fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    SLuint32 txRearNext = txRear+1;
210fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    if (txRearNext > txBufCount) {
211fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        txRearNext = 0;
212fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    }
213fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    assert(txRearNext != txFront);
214fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    txBuffers[txRear] = buffer;
215fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    txRear = txRearNext;
216fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
217fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#else
2187126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // First try to enqueue the free buffer for recording
2190ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes);
2205ef8af762b27b4fa45f59d944b11ad00311cb14bGlenn Kasten    if (SL_RESULT_SUCCESS == result) {
2217126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
2227126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // There was room in the record queue, update our model of it
2237126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(rxFront <= rxBufCount);
2247126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(rxRear <= rxBufCount);
2257126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        SLuint32 rxRearNext = rxRear+1;
2267126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        if (rxRearNext > rxBufCount) {
2277126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            rxRearNext = 0;
2287126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        }
2297126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(rxRearNext != rxFront);
2307126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        rxBuffers[rxRear] = buffer;
2317126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        rxRear = rxRearNext;
2327126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
2330ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    } else {
2347126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
2357126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // Here if record queue is full
2367126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(SL_RESULT_BUFFER_INSUFFICIENT == result);
2377126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
2387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // Instead enqueue the free buffer on the free queue
2397126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(freeFront <= freeBufCount);
2407126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(freeRear <= freeBufCount);
2417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        SLuint32 freeRearNext = freeRear+1;
2427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        if (freeRearNext > freeBufCount) {
2437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            freeRearNext = 0;
2447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        }
2457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // There must always be room in the free queue
2467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(freeRearNext != freeFront);
2477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        freeBuffers[freeRear] = buffer;
2487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        freeRear = freeRearNext;
2497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
2505ef8af762b27b4fa45f59d944b11ad00311cb14bGlenn Kasten    }
251fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#endif
252f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
2537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    pthread_mutex_unlock(&mutex);
254f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten}
255f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
2567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Main program
257f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kastenint main(int argc, char **argv)
258f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten{
25985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    const char *outFileName = NULL;
2600ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    // process command-line options
2610ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    int i;
2620ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    for (i = 1; i < argc; ++i) {
2630ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        char *arg = argv[i];
2647126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        if (arg[0] != '-') {
2650ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            break;
2667126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        }
2677126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // -r# number of slots in receive buffer queue
2680ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        if (!strncmp(arg, "-r", 2)) {
2690ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            rxBufCount = atoi(&arg[2]);
2707126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            if (rxBufCount < 1 || rxBufCount > 16) {
2717126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten                fprintf(stderr, "%s: unusual receive buffer queue size (%u buffers)\n", argv[0],
2720ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                    (unsigned) rxBufCount);
2737126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            }
2747126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // -t# number of slots in transmit buffer queue
2750ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        } else if (!strncmp(arg, "-t", 2)) {
2760ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            txBufCount = atoi(&arg[2]);
2777126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            if (txBufCount < 1 || txBufCount > 16) {
2787126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten                fprintf(stderr, "%s: unusual transmit buffer queue size (%u buffers)\n", argv[0],
2790ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                    (unsigned) txBufCount);
2807126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            }
2810ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        // -f# size of each buffer in frames
2820ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        } else if (!strncmp(arg, "-f", 2)) {
2830ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            bufSizeInFrames = atoi(&arg[2]);
2840ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            if (bufSizeInFrames == 0) {
2850ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                fprintf(stderr, "%s: unusual buffer size (%u frames)\n", argv[0],
2860ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                    (unsigned) bufSizeInFrames);
2870ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            }
2880ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        // -c1 mono or -c2 stereo
2890ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        } else if (!strncmp(arg, "-c", 2)) {
2900ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            channels = atoi(&arg[2]);
2910ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            if (channels < 1 || channels > 2) {
2920ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                fprintf(stderr, "%s: unusual channel count ignored (%u)\n", argv[0],
2930ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                    (unsigned) channels);
2940ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                channels = 2;
2950ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            }
2960ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        // -s# sample rate in Hz
2970ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        } else if (!strncmp(arg, "-s", 2)) {
2980ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            sampleRate = atoi(&arg[2]);
2990ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            switch (sampleRate) {
3000ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            case 8000:
3010ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            case 11025:
3027126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            case 12000:
3030ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            case 16000:
3040ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            case 22050:
3057126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            case 24000:
3060ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            case 32000:
3070ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            case 44100:
3087126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            case 48000:
3090ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                break;
3100ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            default:
3110ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                fprintf(stderr, "%s: unusual sample rate (%u Hz)\n", argv[0],
3120ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                    (unsigned) sampleRate);
3130ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten                break;
3140ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            }
3157126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // -e# exit after this many seconds
3167126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        } else if (!strncmp(arg, "-e", 2)) {
3177126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            exitAfterSeconds = atoi(&arg[2]);
31885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        // -ofile log to output file also
31985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        } else if (!strncmp(arg, "-o", 2)) {
32085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            outFileName = &arg[2];
32185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        // -i# inject an impulse after # milliseconds
32285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        } else if (!strncmp(arg, "-i", 2)) {
32385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            injectImpulse = atoi(&arg[2]);
3240ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        } else
3250ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten            fprintf(stderr, "%s: unknown option %s\n", argv[0], arg);
3260ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    }
3277126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // no other arguments allowed
3280ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    if (i < argc) {
32985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        fprintf(stderr, "usage: %s -r# -t# -f# -s# -c# -i# -ofile\n", argv[0]);
3300ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        fprintf(stderr, "  -r# receive buffer queue count for microphone input, default 1\n");
3310ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        fprintf(stderr, "  -t# transmit buffer queue count for speaker output, default 2\n");
3320ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        fprintf(stderr, "  -f# number of frames per buffer, default 512\n");
3330ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        fprintf(stderr, "  -s# sample rate in Hz, default 44100\n");
3340ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        fprintf(stderr, "  -c1 mono\n");
3350ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        fprintf(stderr, "  -c2 stereo, default\n");
33685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        fprintf(stderr, "  -i# inject impulse after # milliseconds\n");
33785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        fprintf(stderr, "  -ofile log input to specified .wav file also\n");
3380ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    }
33985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten
3407126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // compute total free buffers as -r plus -t
3417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    freeBufCount = rxBufCount + txBufCount;
3427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // compute buffer size
3430ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    bufSizeInBytes = channels * bufSizeInFrames * sizeof(short);
3447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
3457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // Initialize free buffers
3467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    freeBuffers = (char **) calloc(freeBufCount+1, sizeof(char *));
3477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    unsigned j;
3487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    for (j = 0; j < freeBufCount; ++j) {
3497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        freeBuffers[j] = (char *) malloc(bufSizeInBytes);
3507126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    }
3517126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    freeFront = 0;
3527126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    freeRear = freeBufCount;
3537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    freeBuffers[j] = NULL;
3547126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
3557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // Initialize record queue
3567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    rxBuffers = (char **) calloc(rxBufCount+1, sizeof(char *));
3577126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    rxFront = 0;
3587126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    rxRear = 0;
3597126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
3607126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // Initialize play queue
3617126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    txBuffers = (char **) calloc(txBufCount+1, sizeof(char *));
3627126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    txFront = 0;
3637126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    txRear = 0;
3647126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
365fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    const android::NBAIO_Format nbaio_format = android::Format_from_SR_C(sampleRate, channels,
366fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten            AUDIO_FORMAT_PCM_16_BIT);
367fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    pipeWriter = new android::MonoPipe(1024, nbaio_format, false /*writeCanBlock*/);
368fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    android::NBAIO_Format offer = nbaio_format;
369fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    size_t numCounterOffers = 0;
370fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    ssize_t neg = pipeWriter->negotiate(&offer, 1, NULL, numCounterOffers);
371fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    assert(0 == neg);
372fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    pipeReader = new android::MonoPipeReader(pipeWriter);
373fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    numCounterOffers = 0;
374fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    neg = pipeReader->negotiate(&offer, 1, NULL, numCounterOffers);
375fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    assert(0 == neg);
376fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
37785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    SNDFILE *sndfile;
37885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    if (outFileName != NULL) {
37985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        // create .wav writer
38085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        SF_INFO info;
38185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        info.frames = 0;
38285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        info.samplerate = sampleRate;
38385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        info.channels = channels;
38485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
38585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        sndfile = sf_open(outFileName, SFM_WRITE, &info);
38685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        if (sndfile != NULL) {
38785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            pipeWriter2 = new android::MonoPipe(65536, nbaio_format, false /*writeCanBlock*/);
38885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            offer = nbaio_format;
38985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            numCounterOffers = 0;
39085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            neg = pipeWriter2->negotiate(&offer, 1, NULL, numCounterOffers);
39185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            assert(0 == neg);
39285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            pipeReader2 = new android::MonoPipeReader(pipeWriter2);
39385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            numCounterOffers = 0;
39485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            neg = pipeReader2->negotiate(&offer, 1, NULL, numCounterOffers);
39585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            assert(0 == neg);
39685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        } else {
39785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            fprintf(stderr, "sf_open failed\n");
39885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        }
39985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    } else {
40085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        sndfile = NULL;
40185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    }
40285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten
403f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLresult result;
404f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
405f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    // create engine
406f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLObjectItf engineObject;
407f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
408f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
409f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
410f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
411f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLEngineItf engineEngine;
412f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
413f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
414f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
415f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    // create output mix
416f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLObjectItf outputmixObject;
417f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputmixObject, 0, NULL, NULL);
418f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
419f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*outputmixObject)->Realize(outputmixObject, SL_BOOLEAN_FALSE);
420f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
421f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
422f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    // create an audio player with buffer queue source and output mix sink
423f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLDataSource audiosrc;
424f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLDataSink audiosnk;
425f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLDataFormat_PCM pcm;
426f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLDataLocator_OutputMix locator_outputmix;
42701e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    SLDataLocator_BufferQueue locator_bufferqueue_tx;
42801e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    locator_bufferqueue_tx.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
42901e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    locator_bufferqueue_tx.numBuffers = txBufCount;
430f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
431f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    locator_outputmix.outputMix = outputmixObject;
432f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    pcm.formatType = SL_DATAFORMAT_PCM;
4330ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    pcm.numChannels = channels;
4340ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    pcm.samplesPerSec = sampleRate * 1000;
435f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
436f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    pcm.containerSize = 16;
4370ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    pcm.channelMask = channels == 1 ? SL_SPEAKER_FRONT_CENTER :
4380ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
439f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
44001e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    audiosrc.pLocator = &locator_bufferqueue_tx;
441f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    audiosrc.pFormat = &pcm;
442f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    audiosnk.pLocator = &locator_outputmix;
443f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    audiosnk.pFormat = NULL;
4447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    SLObjectItf playerObject = NULL;
4457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    SLObjectItf recorderObject = NULL;
44601e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    SLInterfaceID ids_tx[1] = {SL_IID_BUFFERQUEUE};
44701e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    SLboolean flags_tx[1] = {SL_BOOLEAN_TRUE};
4480ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audiosrc, &audiosnk,
44901e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten        1, ids_tx, flags_tx);
4507126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    if (SL_RESULT_CONTENT_UNSUPPORTED == result) {
45158432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kasten        fprintf(stderr, "Could not create audio player (result %x), check sample rate\n", result);
4527126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        goto cleanup;
4537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    }
454f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
455f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
456f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
457f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLPlayItf playerPlay;
458f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);
459f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
460f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &playerBufferQueue);
461f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
4627126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, playerCallback, NULL);
4637126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
464fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
465fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    // Enqueue some zero buffers for the player
466fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    for (j = 0; j < txBufCount; ++j) {
467fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
468fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        // allocate a free buffer
469fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        assert(freeFront != freeRear);
470fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        char *buffer = freeBuffers[freeFront];
471fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        if (++freeFront > freeBufCount) {
472fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten            freeFront = 0;
473fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        }
474fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
475fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        // put on play queue
476fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        SLuint32 txRearNext = txRear + 1;
477fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        if (txRearNext > txBufCount) {
478fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten            txRearNext = 0;
479fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        }
480fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        assert(txRearNext != txFront);
481fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        txBuffers[txRear] = buffer;
482fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        txRear = txRearNext;
483fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        result = (*playerBufferQueue)->Enqueue(playerBufferQueue,
484fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten            buffer, bufSizeInBytes);
485fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten        ASSERT_EQ(SL_RESULT_SUCCESS, result);
486fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    }
487fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
488f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
489f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
490f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
491f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    // Create an audio recorder with microphone device source and buffer queue sink.
492f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    // The buffer queue as sink is an Android-specific extension.
493f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
494f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLDataLocator_IODevice locator_iodevice;
49501e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    SLDataLocator_AndroidSimpleBufferQueue locator_bufferqueue_rx;
496f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    locator_iodevice.locatorType = SL_DATALOCATOR_IODEVICE;
497f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    locator_iodevice.deviceType = SL_IODEVICE_AUDIOINPUT;
498f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    locator_iodevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
499f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    locator_iodevice.device = NULL;
500f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    audiosrc.pLocator = &locator_iodevice;
50135c82a8173d1fcc18ae0bc08954a018bfce041deGlenn Kasten    audiosrc.pFormat = NULL;
50201e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    locator_bufferqueue_rx.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
50301e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    locator_bufferqueue_rx.numBuffers = rxBufCount;
50401e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    audiosnk.pLocator = &locator_bufferqueue_rx;
505f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    audiosnk.pFormat = &pcm;
5067126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    {
50701e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    SLInterfaceID ids_rx[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
50801e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    SLboolean flags_rx[1] = {SL_BOOLEAN_TRUE};
5090ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten    result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audiosrc,
51001e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten        &audiosnk, 1, ids_rx, flags_rx);
5117126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    if (SL_RESULT_SUCCESS != result) {
51258432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kasten        fprintf(stderr, "Could not create audio recorder (result %x), "
5137126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten                "check sample rate and channel count\n", result);
5147126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        goto cleanup;
5157126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    }
5167126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    }
517f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
518f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
519f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
520f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    SLRecordItf recorderRecord;
521f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
522f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
52301e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten    result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
5240ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        &recorderBufferQueue);
525f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
526f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, recorderCallback, NULL);
527f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
528f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
529f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    // Enqueue some empty buffers for the recorder
5307126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    for (j = 0; j < rxBufCount; ++j) {
5317126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
5327126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // allocate a free buffer
5337126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(freeFront != freeRear);
5347126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        char *buffer = freeBuffers[freeFront];
5357126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        if (++freeFront > freeBufCount) {
5367126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            freeFront = 0;
5377126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        }
5387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
5397126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        // put on record queue
5407126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        SLuint32 rxRearNext = rxRear + 1;
5417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        if (rxRearNext > rxBufCount) {
5427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            rxRearNext = 0;
5437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        }
5447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        assert(rxRearNext != rxFront);
5457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        rxBuffers[rxRear] = buffer;
5467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        rxRear = rxRearNext;
5470ac2a7d4343d98e3cb02180e548a5a4737ba0df1Glenn Kasten        result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue,
5487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten            buffer, bufSizeInBytes);
549f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten        ASSERT_EQ(SL_RESULT_SUCCESS, result);
550f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    }
551f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
552f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    // Kick off the recorder
553f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
554f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    ASSERT_EQ(SL_RESULT_SUCCESS, result);
555f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
556fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#if 0
557fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    // give recorder a head start so that the pipe is initially filled
558fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten    sleep(1);
559fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten#endif
560fdef5de17abc6c30b293861ca276259a7dd93837Glenn Kasten
561f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    // Wait patiently
5627126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    do {
56385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        for (int i = 0; i < 10; i++) {
56485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            usleep(100000);
56585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            if (pipeReader2 != NULL) {
56685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                for (;;) {
56785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    short buffer[bufSizeInFrames * channels];
56885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    ssize_t actual = pipeReader2->read(buffer, bufSizeInFrames, (int64_t) -1);
56985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    if (actual <= 0)
57085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                        break;
57185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    (void) sf_writef_short(sndfile, buffer, (sf_count_t) actual);
57285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                }
57385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            }
57485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            if (injectImpulse > 0) {
57585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                if (injectImpulse <= 100) {
57685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    injectImpulse = -1;
57785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    write(1, "I", 1);
57885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                } else {
57985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    if ((injectImpulse % 1000) < 100) {
58085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                        write(1, "i", 1);
58185079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    }
58285079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                    injectImpulse -= 100;
58385079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                }
58485079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            } else if (i == 9) {
58585079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten                write(1, ".", 1);
58685079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten            }
58785079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        }
588f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten        SLBufferQueueState playerBQState;
589f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten        result = (*playerBufferQueue)->GetState(playerBufferQueue, &playerBQState);
590f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten        ASSERT_EQ(SL_RESULT_SUCCESS, result);
59101e9f5fa4698856f92bcfd88188ee4c8397b22dbGlenn Kasten        SLAndroidSimpleBufferQueueState recorderBQState;
592f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten        result = (*recorderBufferQueue)->GetState(recorderBufferQueue, &recorderBQState);
593f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten        ASSERT_EQ(SL_RESULT_SUCCESS, result);
5947126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    } while (--exitAfterSeconds);
5957126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
5967126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    // Tear down the objects and exit
5977126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastencleanup:
59885079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    if (sndfile != NULL) {
59985079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten        sf_close(sndfile);
60085079ea0cc7592029762b7c44a6def84c43bebb0Glenn Kasten    }
6017126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    if (NULL != playerObject) {
6027126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        (*playerObject)->Destroy(playerObject);
6037126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    }
6047126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    if (NULL != recorderObject) {
6057126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        (*recorderObject)->Destroy(recorderObject);
606f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten    }
6077126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    (*outputmixObject)->Destroy(outputmixObject);
6087126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    (*engineObject)->Destroy(engineObject);
609f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten
6107126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    return EXIT_SUCCESS;
611f460ec604707d0bdaf8124d84c5f8595cba9c804Glenn Kasten}
612