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