xref: /aosp_15_r20/frameworks/wilhelm/tests/sandbox/playbq.cpp (revision bebae9c0e76121f8312ccb50385c080b3a0b023c)
1*bebae9c0SAndroid Build Coastguard Worker /*
2*bebae9c0SAndroid Build Coastguard Worker  * Copyright (C) 2010 The Android Open Source Project
3*bebae9c0SAndroid Build Coastguard Worker  *
4*bebae9c0SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*bebae9c0SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*bebae9c0SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*bebae9c0SAndroid Build Coastguard Worker  *
8*bebae9c0SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*bebae9c0SAndroid Build Coastguard Worker  *
10*bebae9c0SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*bebae9c0SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*bebae9c0SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*bebae9c0SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*bebae9c0SAndroid Build Coastguard Worker  * limitations under the License.
15*bebae9c0SAndroid Build Coastguard Worker  */
16*bebae9c0SAndroid Build Coastguard Worker 
17*bebae9c0SAndroid Build Coastguard Worker // Play an audio file using buffer queue
18*bebae9c0SAndroid Build Coastguard Worker 
19*bebae9c0SAndroid Build Coastguard Worker #include <assert.h>
20*bebae9c0SAndroid Build Coastguard Worker #include <math.h>
21*bebae9c0SAndroid Build Coastguard Worker #include <pthread.h>
22*bebae9c0SAndroid Build Coastguard Worker #include <stdio.h>
23*bebae9c0SAndroid Build Coastguard Worker #include <stdlib.h>
24*bebae9c0SAndroid Build Coastguard Worker #include <string.h>
25*bebae9c0SAndroid Build Coastguard Worker #include <time.h>
26*bebae9c0SAndroid Build Coastguard Worker #include <unistd.h>
27*bebae9c0SAndroid Build Coastguard Worker 
28*bebae9c0SAndroid Build Coastguard Worker #include <SLES/OpenSLES.h>
29*bebae9c0SAndroid Build Coastguard Worker #include <SLES/OpenSLES_Android.h>
30*bebae9c0SAndroid Build Coastguard Worker #include <system/audio.h>
31*bebae9c0SAndroid Build Coastguard Worker #include <audio_utils/fifo.h>
32*bebae9c0SAndroid Build Coastguard Worker #include <audio_utils/primitives.h>
33*bebae9c0SAndroid Build Coastguard Worker #include <audio_utils/sndfile.h>
34*bebae9c0SAndroid Build Coastguard Worker 
35*bebae9c0SAndroid Build Coastguard Worker #define max(a, b) ((a) > (b) ? (a) : (b))
36*bebae9c0SAndroid Build Coastguard Worker #define min(a, b) ((a) < (b) ? (a) : (b))
37*bebae9c0SAndroid Build Coastguard Worker 
38*bebae9c0SAndroid Build Coastguard Worker unsigned numBuffers = 2;
39*bebae9c0SAndroid Build Coastguard Worker int framesPerBuffer = 512;
40*bebae9c0SAndroid Build Coastguard Worker SNDFILE *sndfile;
41*bebae9c0SAndroid Build Coastguard Worker SF_INFO sfinfo;
42*bebae9c0SAndroid Build Coastguard Worker unsigned which; // which buffer to use next
43*bebae9c0SAndroid Build Coastguard Worker SLboolean eof;  // whether we have hit EOF on input yet
44*bebae9c0SAndroid Build Coastguard Worker void *buffers;
45*bebae9c0SAndroid Build Coastguard Worker SLuint32 byteOrder; // desired to use for PCM buffers
46*bebae9c0SAndroid Build Coastguard Worker SLuint32 nativeByteOrder;   // of platform
47*bebae9c0SAndroid Build Coastguard Worker audio_format_t transferFormat = AUDIO_FORMAT_DEFAULT;
48*bebae9c0SAndroid Build Coastguard Worker size_t sfframesize = 0;
49*bebae9c0SAndroid Build Coastguard Worker 
50*bebae9c0SAndroid Build Coastguard Worker static audio_utils_fifo *fifo;
51*bebae9c0SAndroid Build Coastguard Worker static audio_utils_fifo_reader *fifoReader;
52*bebae9c0SAndroid Build Coastguard Worker static audio_utils_fifo_writer *fifoWriter;
53*bebae9c0SAndroid Build Coastguard Worker static unsigned underruns = 0;
54*bebae9c0SAndroid Build Coastguard Worker 
squeeze(void * buffer,SLuint32 nbytes)55*bebae9c0SAndroid Build Coastguard Worker static SLuint32 squeeze(void *buffer, SLuint32 nbytes)
56*bebae9c0SAndroid Build Coastguard Worker {
57*bebae9c0SAndroid Build Coastguard Worker     if (byteOrder != nativeByteOrder) {
58*bebae9c0SAndroid Build Coastguard Worker         // FIXME does not work for non 16-bit
59*bebae9c0SAndroid Build Coastguard Worker         swab(buffer, buffer, nbytes);
60*bebae9c0SAndroid Build Coastguard Worker     }
61*bebae9c0SAndroid Build Coastguard Worker     if (transferFormat == AUDIO_FORMAT_PCM_8_BIT) {
62*bebae9c0SAndroid Build Coastguard Worker         memcpy_to_u8_from_i16((uint8_t *) buffer, (const int16_t *) buffer,
63*bebae9c0SAndroid Build Coastguard Worker                 nbytes / sizeof(int16_t));
64*bebae9c0SAndroid Build Coastguard Worker         nbytes /= 2;
65*bebae9c0SAndroid Build Coastguard Worker     } else if (transferFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) {
66*bebae9c0SAndroid Build Coastguard Worker         memcpy_to_p24_from_i32((uint8_t *) buffer, (const int32_t *) buffer,
67*bebae9c0SAndroid Build Coastguard Worker                 nbytes / sizeof(int32_t));
68*bebae9c0SAndroid Build Coastguard Worker         nbytes = nbytes * 3 / 4;
69*bebae9c0SAndroid Build Coastguard Worker     }
70*bebae9c0SAndroid Build Coastguard Worker     return nbytes;
71*bebae9c0SAndroid Build Coastguard Worker }
72*bebae9c0SAndroid Build Coastguard Worker 
73*bebae9c0SAndroid Build Coastguard Worker // This callback is called each time a buffer finishes playing
74*bebae9c0SAndroid Build Coastguard Worker 
callback(SLBufferQueueItf bufq,void * param)75*bebae9c0SAndroid Build Coastguard Worker static void callback(SLBufferQueueItf bufq, void *param)
76*bebae9c0SAndroid Build Coastguard Worker {
77*bebae9c0SAndroid Build Coastguard Worker     assert(NULL == param);
78*bebae9c0SAndroid Build Coastguard Worker     if (!eof) {
79*bebae9c0SAndroid Build Coastguard Worker         void *buffer = (char *)buffers + framesPerBuffer * sfframesize * which;
80*bebae9c0SAndroid Build Coastguard Worker         ssize_t count = fifoReader->read(buffer, framesPerBuffer);
81*bebae9c0SAndroid Build Coastguard Worker         // on underrun from pipe, substitute silence
82*bebae9c0SAndroid Build Coastguard Worker         if (0 >= count) {
83*bebae9c0SAndroid Build Coastguard Worker             memset(buffer, 0, framesPerBuffer * sfframesize);
84*bebae9c0SAndroid Build Coastguard Worker             count = framesPerBuffer;
85*bebae9c0SAndroid Build Coastguard Worker             ++underruns;
86*bebae9c0SAndroid Build Coastguard Worker         }
87*bebae9c0SAndroid Build Coastguard Worker         if (count > 0) {
88*bebae9c0SAndroid Build Coastguard Worker             SLuint32 nbytes = count * sfframesize;
89*bebae9c0SAndroid Build Coastguard Worker             nbytes = squeeze(buffer, nbytes);
90*bebae9c0SAndroid Build Coastguard Worker             SLresult result = (*bufq)->Enqueue(bufq, buffer, nbytes);
91*bebae9c0SAndroid Build Coastguard Worker             assert(SL_RESULT_SUCCESS == result);
92*bebae9c0SAndroid Build Coastguard Worker             if (++which >= numBuffers)
93*bebae9c0SAndroid Build Coastguard Worker                 which = 0;
94*bebae9c0SAndroid Build Coastguard Worker         }
95*bebae9c0SAndroid Build Coastguard Worker     }
96*bebae9c0SAndroid Build Coastguard Worker }
97*bebae9c0SAndroid Build Coastguard Worker 
98*bebae9c0SAndroid Build Coastguard Worker // This thread reads from a (slow) filesystem with unpredictable latency and writes to pipe
99*bebae9c0SAndroid Build Coastguard Worker 
file_reader_loop(void * arg __unused)100*bebae9c0SAndroid Build Coastguard Worker static void *file_reader_loop(void *arg __unused)
101*bebae9c0SAndroid Build Coastguard Worker {
102*bebae9c0SAndroid Build Coastguard Worker #define READ_FRAMES 256
103*bebae9c0SAndroid Build Coastguard Worker     void *temp = malloc(READ_FRAMES * sfframesize);
104*bebae9c0SAndroid Build Coastguard Worker     sf_count_t total = 0;
105*bebae9c0SAndroid Build Coastguard Worker     sf_count_t count;
106*bebae9c0SAndroid Build Coastguard Worker     for (;;) {
107*bebae9c0SAndroid Build Coastguard Worker         switch (transferFormat) {
108*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_FLOAT:
109*bebae9c0SAndroid Build Coastguard Worker             count = sf_readf_float(sndfile, (float *) temp, READ_FRAMES);
110*bebae9c0SAndroid Build Coastguard Worker             break;
111*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_32_BIT:
112*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_24_BIT_PACKED:
113*bebae9c0SAndroid Build Coastguard Worker             count = sf_readf_int(sndfile, (int *) temp, READ_FRAMES);
114*bebae9c0SAndroid Build Coastguard Worker             break;
115*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_16_BIT:
116*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_8_BIT:
117*bebae9c0SAndroid Build Coastguard Worker             count = sf_readf_short(sndfile, (short *) temp, READ_FRAMES);
118*bebae9c0SAndroid Build Coastguard Worker             break;
119*bebae9c0SAndroid Build Coastguard Worker         default:
120*bebae9c0SAndroid Build Coastguard Worker             count = 0;
121*bebae9c0SAndroid Build Coastguard Worker             break;
122*bebae9c0SAndroid Build Coastguard Worker         }
123*bebae9c0SAndroid Build Coastguard Worker         if (0 >= count) {
124*bebae9c0SAndroid Build Coastguard Worker             eof = SL_BOOLEAN_TRUE;
125*bebae9c0SAndroid Build Coastguard Worker             break;
126*bebae9c0SAndroid Build Coastguard Worker         }
127*bebae9c0SAndroid Build Coastguard Worker         const unsigned char *ptr = (unsigned char *) temp;
128*bebae9c0SAndroid Build Coastguard Worker         while (count > 0) {
129*bebae9c0SAndroid Build Coastguard Worker             ssize_t actual = fifoWriter->write(ptr, (size_t) count);
130*bebae9c0SAndroid Build Coastguard Worker             if (actual < 0) {
131*bebae9c0SAndroid Build Coastguard Worker                 break;
132*bebae9c0SAndroid Build Coastguard Worker             }
133*bebae9c0SAndroid Build Coastguard Worker             if ((sf_count_t) actual < count) {
134*bebae9c0SAndroid Build Coastguard Worker                 usleep(10000);
135*bebae9c0SAndroid Build Coastguard Worker             }
136*bebae9c0SAndroid Build Coastguard Worker             ptr += actual * sfframesize;
137*bebae9c0SAndroid Build Coastguard Worker             count -= actual;
138*bebae9c0SAndroid Build Coastguard Worker             total += actual;
139*bebae9c0SAndroid Build Coastguard Worker         }
140*bebae9c0SAndroid Build Coastguard Worker         // simulate occasional filesystem latency
141*bebae9c0SAndroid Build Coastguard Worker         if ((total & 0xFF00) == 0xFF00) {
142*bebae9c0SAndroid Build Coastguard Worker             usleep(100000);
143*bebae9c0SAndroid Build Coastguard Worker         }
144*bebae9c0SAndroid Build Coastguard Worker     }
145*bebae9c0SAndroid Build Coastguard Worker     free(temp);
146*bebae9c0SAndroid Build Coastguard Worker     return NULL;
147*bebae9c0SAndroid Build Coastguard Worker }
148*bebae9c0SAndroid Build Coastguard Worker 
149*bebae9c0SAndroid Build Coastguard Worker // Main program
150*bebae9c0SAndroid Build Coastguard Worker 
main(int argc,char ** argv)151*bebae9c0SAndroid Build Coastguard Worker int main(int argc, char **argv)
152*bebae9c0SAndroid Build Coastguard Worker {
153*bebae9c0SAndroid Build Coastguard Worker     // Determine the native byte order (SL_BYTEORDER_NATIVE not available until 1.1)
154*bebae9c0SAndroid Build Coastguard Worker     union {
155*bebae9c0SAndroid Build Coastguard Worker         short s;
156*bebae9c0SAndroid Build Coastguard Worker         char c[2];
157*bebae9c0SAndroid Build Coastguard Worker     } u;
158*bebae9c0SAndroid Build Coastguard Worker     u.s = 0x1234;
159*bebae9c0SAndroid Build Coastguard Worker     if (u.c[0] == 0x34) {
160*bebae9c0SAndroid Build Coastguard Worker         nativeByteOrder = SL_BYTEORDER_LITTLEENDIAN;
161*bebae9c0SAndroid Build Coastguard Worker     } else if (u.c[0] == 0x12) {
162*bebae9c0SAndroid Build Coastguard Worker         nativeByteOrder = SL_BYTEORDER_BIGENDIAN;
163*bebae9c0SAndroid Build Coastguard Worker     } else {
164*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "Unable to determine native byte order\n");
165*bebae9c0SAndroid Build Coastguard Worker         return EXIT_FAILURE;
166*bebae9c0SAndroid Build Coastguard Worker     }
167*bebae9c0SAndroid Build Coastguard Worker     byteOrder = nativeByteOrder;
168*bebae9c0SAndroid Build Coastguard Worker 
169*bebae9c0SAndroid Build Coastguard Worker     SLboolean enableReverb = SL_BOOLEAN_FALSE;
170*bebae9c0SAndroid Build Coastguard Worker     SLboolean enablePlaybackRate = SL_BOOLEAN_FALSE;
171*bebae9c0SAndroid Build Coastguard Worker     SLpermille initialRate = 0;
172*bebae9c0SAndroid Build Coastguard Worker     SLpermille finalRate = 0;
173*bebae9c0SAndroid Build Coastguard Worker     SLpermille deltaRate = 1;
174*bebae9c0SAndroid Build Coastguard Worker     SLmillisecond deltaRateMs = 0;
175*bebae9c0SAndroid Build Coastguard Worker 
176*bebae9c0SAndroid Build Coastguard Worker     // process command-line options
177*bebae9c0SAndroid Build Coastguard Worker     int i;
178*bebae9c0SAndroid Build Coastguard Worker     for (i = 1; i < argc; ++i) {
179*bebae9c0SAndroid Build Coastguard Worker         char *arg = argv[i];
180*bebae9c0SAndroid Build Coastguard Worker         if (arg[0] != '-') {
181*bebae9c0SAndroid Build Coastguard Worker             break;
182*bebae9c0SAndroid Build Coastguard Worker         }
183*bebae9c0SAndroid Build Coastguard Worker         if (!strcmp(arg, "-b")) {
184*bebae9c0SAndroid Build Coastguard Worker             byteOrder = SL_BYTEORDER_BIGENDIAN;
185*bebae9c0SAndroid Build Coastguard Worker         } else if (!strcmp(arg, "-l")) {
186*bebae9c0SAndroid Build Coastguard Worker             byteOrder = SL_BYTEORDER_LITTLEENDIAN;
187*bebae9c0SAndroid Build Coastguard Worker         } else if (!strcmp(arg, "-8")) {
188*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_8_BIT;
189*bebae9c0SAndroid Build Coastguard Worker         } else if (!strcmp(arg, "-16")) {
190*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_16_BIT;
191*bebae9c0SAndroid Build Coastguard Worker         } else if (!strcmp(arg, "-24")) {
192*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_24_BIT_PACKED;
193*bebae9c0SAndroid Build Coastguard Worker         } else if (!strcmp(arg, "-32")) {
194*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_32_BIT;
195*bebae9c0SAndroid Build Coastguard Worker         } else if (!strcmp(arg, "-32f")) {
196*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_FLOAT;
197*bebae9c0SAndroid Build Coastguard Worker         } else if (!strncmp(arg, "-f", 2)) {
198*bebae9c0SAndroid Build Coastguard Worker             framesPerBuffer = atoi(&arg[2]);
199*bebae9c0SAndroid Build Coastguard Worker         } else if (!strncmp(arg, "-n", 2)) {
200*bebae9c0SAndroid Build Coastguard Worker             numBuffers = atoi(&arg[2]);
201*bebae9c0SAndroid Build Coastguard Worker         } else if (!strncmp(arg, "-p", 2)) {
202*bebae9c0SAndroid Build Coastguard Worker             initialRate = atoi(&arg[2]);
203*bebae9c0SAndroid Build Coastguard Worker             enablePlaybackRate = SL_BOOLEAN_TRUE;
204*bebae9c0SAndroid Build Coastguard Worker         } else if (!strncmp(arg, "-P", 2)) {
205*bebae9c0SAndroid Build Coastguard Worker             finalRate = atoi(&arg[2]);
206*bebae9c0SAndroid Build Coastguard Worker             enablePlaybackRate = SL_BOOLEAN_TRUE;
207*bebae9c0SAndroid Build Coastguard Worker         } else if (!strncmp(arg, "-q", 2)) {
208*bebae9c0SAndroid Build Coastguard Worker             deltaRate = atoi(&arg[2]);
209*bebae9c0SAndroid Build Coastguard Worker             // deltaRate is a magnitude, so take absolute value
210*bebae9c0SAndroid Build Coastguard Worker             if (deltaRate < 0) {
211*bebae9c0SAndroid Build Coastguard Worker                 deltaRate = -deltaRate;
212*bebae9c0SAndroid Build Coastguard Worker             }
213*bebae9c0SAndroid Build Coastguard Worker             enablePlaybackRate = SL_BOOLEAN_TRUE;
214*bebae9c0SAndroid Build Coastguard Worker         } else if (!strncmp(arg, "-Q", 2)) {
215*bebae9c0SAndroid Build Coastguard Worker             deltaRateMs = atoi(&arg[2]);
216*bebae9c0SAndroid Build Coastguard Worker             enablePlaybackRate = SL_BOOLEAN_TRUE;
217*bebae9c0SAndroid Build Coastguard Worker         } else if (!strcmp(arg, "-r")) {
218*bebae9c0SAndroid Build Coastguard Worker             enableReverb = SL_BOOLEAN_TRUE;
219*bebae9c0SAndroid Build Coastguard Worker         } else {
220*bebae9c0SAndroid Build Coastguard Worker             fprintf(stderr, "option %s ignored\n", arg);
221*bebae9c0SAndroid Build Coastguard Worker         }
222*bebae9c0SAndroid Build Coastguard Worker     }
223*bebae9c0SAndroid Build Coastguard Worker 
224*bebae9c0SAndroid Build Coastguard Worker     if (argc - i != 1) {
225*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "usage: [-b/l] [-8 | | -16 | -24 | -32 | -32f] [-f#] [-n#] [-p#] [-r]"
226*bebae9c0SAndroid Build Coastguard Worker                 " %s filename\n", argv[0]);
227*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -b  force big-endian byte order (default is native byte order)\n");
228*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -l  force little-endian byte order (default is native byte order)\n");
229*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -8  output 8-bits per sample (default is that of input file)\n");
230*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -16 output 16-bits per sample\n");
231*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -24 output 24-bits per sample\n");
232*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -32 output 32-bits per sample\n");
233*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -32f output float 32-bits per sample\n");
234*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -f# frames per buffer (default 512)\n");
235*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -n# number of buffers (default 2)\n");
236*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -p# initial playback rate in per mille (default 1000)\n");
237*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -P# final playback rate in per mille (default same as -p#)\n");
238*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -q# magnitude of playback rate changes in per mille (default 1)\n");
239*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -Q# period between playback rate changes in ms (default 50)\n");
240*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "    -r  enable reverb (default disabled)\n");
241*bebae9c0SAndroid Build Coastguard Worker         return EXIT_FAILURE;
242*bebae9c0SAndroid Build Coastguard Worker     }
243*bebae9c0SAndroid Build Coastguard Worker 
244*bebae9c0SAndroid Build Coastguard Worker     const char *filename = argv[i];
245*bebae9c0SAndroid Build Coastguard Worker     //memset(&sfinfo, 0, sizeof(SF_INFO));
246*bebae9c0SAndroid Build Coastguard Worker     sfinfo.format = 0;
247*bebae9c0SAndroid Build Coastguard Worker     sndfile = sf_open(filename, SFM_READ, &sfinfo);
248*bebae9c0SAndroid Build Coastguard Worker     if (NULL == sndfile) {
249*bebae9c0SAndroid Build Coastguard Worker         perror(filename);
250*bebae9c0SAndroid Build Coastguard Worker         return EXIT_FAILURE;
251*bebae9c0SAndroid Build Coastguard Worker     }
252*bebae9c0SAndroid Build Coastguard Worker 
253*bebae9c0SAndroid Build Coastguard Worker     // verify the file format
254*bebae9c0SAndroid Build Coastguard Worker     switch (sfinfo.channels) {
255*bebae9c0SAndroid Build Coastguard Worker     case 1:
256*bebae9c0SAndroid Build Coastguard Worker     case 2:
257*bebae9c0SAndroid Build Coastguard Worker         break;
258*bebae9c0SAndroid Build Coastguard Worker     default:
259*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "unsupported channel count %d\n", sfinfo.channels);
260*bebae9c0SAndroid Build Coastguard Worker         goto close_sndfile;
261*bebae9c0SAndroid Build Coastguard Worker     }
262*bebae9c0SAndroid Build Coastguard Worker 
263*bebae9c0SAndroid Build Coastguard Worker     if (sfinfo.samplerate < 8000 || sfinfo.samplerate > 192000) {
264*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "unsupported sample rate %d\n", sfinfo.samplerate);
265*bebae9c0SAndroid Build Coastguard Worker         goto close_sndfile;
266*bebae9c0SAndroid Build Coastguard Worker     }
267*bebae9c0SAndroid Build Coastguard Worker 
268*bebae9c0SAndroid Build Coastguard Worker     switch (sfinfo.format & SF_FORMAT_TYPEMASK) {
269*bebae9c0SAndroid Build Coastguard Worker     case SF_FORMAT_WAV:
270*bebae9c0SAndroid Build Coastguard Worker         break;
271*bebae9c0SAndroid Build Coastguard Worker     default:
272*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "unsupported format type 0x%x\n", sfinfo.format & SF_FORMAT_TYPEMASK);
273*bebae9c0SAndroid Build Coastguard Worker         goto close_sndfile;
274*bebae9c0SAndroid Build Coastguard Worker     }
275*bebae9c0SAndroid Build Coastguard Worker 
276*bebae9c0SAndroid Build Coastguard Worker     switch (sfinfo.format & SF_FORMAT_SUBMASK) {
277*bebae9c0SAndroid Build Coastguard Worker     case SF_FORMAT_FLOAT:
278*bebae9c0SAndroid Build Coastguard Worker         if (transferFormat == AUDIO_FORMAT_DEFAULT) {
279*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_FLOAT;
280*bebae9c0SAndroid Build Coastguard Worker         }
281*bebae9c0SAndroid Build Coastguard Worker         break;
282*bebae9c0SAndroid Build Coastguard Worker     case SF_FORMAT_PCM_32:
283*bebae9c0SAndroid Build Coastguard Worker         if (transferFormat == AUDIO_FORMAT_DEFAULT) {
284*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_32_BIT;
285*bebae9c0SAndroid Build Coastguard Worker         }
286*bebae9c0SAndroid Build Coastguard Worker         break;
287*bebae9c0SAndroid Build Coastguard Worker     case SF_FORMAT_PCM_16:
288*bebae9c0SAndroid Build Coastguard Worker         if (transferFormat == AUDIO_FORMAT_DEFAULT) {
289*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_16_BIT;
290*bebae9c0SAndroid Build Coastguard Worker         }
291*bebae9c0SAndroid Build Coastguard Worker         break;
292*bebae9c0SAndroid Build Coastguard Worker     case SF_FORMAT_PCM_U8:
293*bebae9c0SAndroid Build Coastguard Worker         if (transferFormat == AUDIO_FORMAT_DEFAULT) {
294*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_8_BIT;
295*bebae9c0SAndroid Build Coastguard Worker         }
296*bebae9c0SAndroid Build Coastguard Worker         break;
297*bebae9c0SAndroid Build Coastguard Worker     case SF_FORMAT_PCM_24:
298*bebae9c0SAndroid Build Coastguard Worker         if (transferFormat == AUDIO_FORMAT_DEFAULT) {
299*bebae9c0SAndroid Build Coastguard Worker             transferFormat = AUDIO_FORMAT_PCM_24_BIT_PACKED;
300*bebae9c0SAndroid Build Coastguard Worker         }
301*bebae9c0SAndroid Build Coastguard Worker         break;
302*bebae9c0SAndroid Build Coastguard Worker     default:
303*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "unsupported sub-format 0x%x\n", sfinfo.format & SF_FORMAT_SUBMASK);
304*bebae9c0SAndroid Build Coastguard Worker         goto close_sndfile;
305*bebae9c0SAndroid Build Coastguard Worker     }
306*bebae9c0SAndroid Build Coastguard Worker 
307*bebae9c0SAndroid Build Coastguard Worker     SLuint32 bitsPerSample;
308*bebae9c0SAndroid Build Coastguard Worker     switch (transferFormat) {
309*bebae9c0SAndroid Build Coastguard Worker     case AUDIO_FORMAT_PCM_FLOAT:
310*bebae9c0SAndroid Build Coastguard Worker         bitsPerSample = 32;
311*bebae9c0SAndroid Build Coastguard Worker         sfframesize = sfinfo.channels * sizeof(float);
312*bebae9c0SAndroid Build Coastguard Worker         break;
313*bebae9c0SAndroid Build Coastguard Worker     case AUDIO_FORMAT_PCM_32_BIT:
314*bebae9c0SAndroid Build Coastguard Worker         bitsPerSample = 32;
315*bebae9c0SAndroid Build Coastguard Worker         sfframesize = sfinfo.channels * sizeof(int);
316*bebae9c0SAndroid Build Coastguard Worker         break;
317*bebae9c0SAndroid Build Coastguard Worker     case AUDIO_FORMAT_PCM_24_BIT_PACKED:
318*bebae9c0SAndroid Build Coastguard Worker         bitsPerSample = 24;
319*bebae9c0SAndroid Build Coastguard Worker         sfframesize = sfinfo.channels * sizeof(int); // use int size
320*bebae9c0SAndroid Build Coastguard Worker         break;
321*bebae9c0SAndroid Build Coastguard Worker     case AUDIO_FORMAT_PCM_16_BIT:
322*bebae9c0SAndroid Build Coastguard Worker         bitsPerSample = 16;
323*bebae9c0SAndroid Build Coastguard Worker         sfframesize = sfinfo.channels * sizeof(short);
324*bebae9c0SAndroid Build Coastguard Worker         break;
325*bebae9c0SAndroid Build Coastguard Worker     case AUDIO_FORMAT_PCM_8_BIT:
326*bebae9c0SAndroid Build Coastguard Worker         bitsPerSample = 8;
327*bebae9c0SAndroid Build Coastguard Worker         sfframesize = sfinfo.channels * sizeof(short); // use short size
328*bebae9c0SAndroid Build Coastguard Worker         break;
329*bebae9c0SAndroid Build Coastguard Worker     default:
330*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "unsupported transfer format %#x\n", transferFormat);
331*bebae9c0SAndroid Build Coastguard Worker         goto close_sndfile;
332*bebae9c0SAndroid Build Coastguard Worker     }
333*bebae9c0SAndroid Build Coastguard Worker 
334*bebae9c0SAndroid Build Coastguard Worker     {
335*bebae9c0SAndroid Build Coastguard Worker     buffers = malloc(framesPerBuffer * sfframesize * numBuffers);
336*bebae9c0SAndroid Build Coastguard Worker 
337*bebae9c0SAndroid Build Coastguard Worker     // create engine
338*bebae9c0SAndroid Build Coastguard Worker     SLresult result;
339*bebae9c0SAndroid Build Coastguard Worker     SLObjectItf engineObject;
340*bebae9c0SAndroid Build Coastguard Worker     result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
341*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
342*bebae9c0SAndroid Build Coastguard Worker     SLEngineItf engineEngine;
343*bebae9c0SAndroid Build Coastguard Worker     result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
344*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
345*bebae9c0SAndroid Build Coastguard Worker     result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
346*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
347*bebae9c0SAndroid Build Coastguard Worker 
348*bebae9c0SAndroid Build Coastguard Worker     // create output mix
349*bebae9c0SAndroid Build Coastguard Worker     SLObjectItf outputMixObject;
350*bebae9c0SAndroid Build Coastguard Worker     SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};
351*bebae9c0SAndroid Build Coastguard Worker     SLboolean req[1] = {SL_BOOLEAN_TRUE};
352*bebae9c0SAndroid Build Coastguard Worker     result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, enableReverb ? 1 : 0,
353*bebae9c0SAndroid Build Coastguard Worker             ids, req);
354*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
355*bebae9c0SAndroid Build Coastguard Worker     result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
356*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
357*bebae9c0SAndroid Build Coastguard Worker 
358*bebae9c0SAndroid Build Coastguard Worker     // configure environmental reverb on output mix
359*bebae9c0SAndroid Build Coastguard Worker     SLEnvironmentalReverbItf mixEnvironmentalReverb = NULL;
360*bebae9c0SAndroid Build Coastguard Worker     if (enableReverb) {
361*bebae9c0SAndroid Build Coastguard Worker         result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
362*bebae9c0SAndroid Build Coastguard Worker                 &mixEnvironmentalReverb);
363*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
364*bebae9c0SAndroid Build Coastguard Worker         SLEnvironmentalReverbSettings settings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;
365*bebae9c0SAndroid Build Coastguard Worker         result = (*mixEnvironmentalReverb)->SetEnvironmentalReverbProperties(mixEnvironmentalReverb,
366*bebae9c0SAndroid Build Coastguard Worker                 &settings);
367*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
368*bebae9c0SAndroid Build Coastguard Worker     }
369*bebae9c0SAndroid Build Coastguard Worker 
370*bebae9c0SAndroid Build Coastguard Worker     // configure audio source
371*bebae9c0SAndroid Build Coastguard Worker     SLDataLocator_BufferQueue loc_bufq;
372*bebae9c0SAndroid Build Coastguard Worker     loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
373*bebae9c0SAndroid Build Coastguard Worker     loc_bufq.numBuffers = numBuffers;
374*bebae9c0SAndroid Build Coastguard Worker     SLAndroidDataFormat_PCM_EX format_pcm;
375*bebae9c0SAndroid Build Coastguard Worker     format_pcm.formatType = transferFormat == AUDIO_FORMAT_PCM_FLOAT
376*bebae9c0SAndroid Build Coastguard Worker             ? SL_ANDROID_DATAFORMAT_PCM_EX : SL_DATAFORMAT_PCM;
377*bebae9c0SAndroid Build Coastguard Worker     format_pcm.numChannels = sfinfo.channels;
378*bebae9c0SAndroid Build Coastguard Worker     format_pcm.sampleRate = sfinfo.samplerate * 1000;
379*bebae9c0SAndroid Build Coastguard Worker     format_pcm.bitsPerSample = bitsPerSample;
380*bebae9c0SAndroid Build Coastguard Worker     format_pcm.containerSize = format_pcm.bitsPerSample;
381*bebae9c0SAndroid Build Coastguard Worker     format_pcm.channelMask = 1 == format_pcm.numChannels ? SL_SPEAKER_FRONT_CENTER :
382*bebae9c0SAndroid Build Coastguard Worker             SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
383*bebae9c0SAndroid Build Coastguard Worker     format_pcm.endianness = byteOrder;
384*bebae9c0SAndroid Build Coastguard Worker     format_pcm.representation = transferFormat == AUDIO_FORMAT_PCM_FLOAT
385*bebae9c0SAndroid Build Coastguard Worker             ? SL_ANDROID_PCM_REPRESENTATION_FLOAT : transferFormat == AUDIO_FORMAT_PCM_8_BIT
386*bebae9c0SAndroid Build Coastguard Worker                     ? SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT
387*bebae9c0SAndroid Build Coastguard Worker                             : SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
388*bebae9c0SAndroid Build Coastguard Worker     SLDataSource audioSrc;
389*bebae9c0SAndroid Build Coastguard Worker     audioSrc.pLocator = &loc_bufq;
390*bebae9c0SAndroid Build Coastguard Worker     audioSrc.pFormat = &format_pcm;
391*bebae9c0SAndroid Build Coastguard Worker 
392*bebae9c0SAndroid Build Coastguard Worker     // configure audio sink
393*bebae9c0SAndroid Build Coastguard Worker     SLDataLocator_OutputMix loc_outmix;
394*bebae9c0SAndroid Build Coastguard Worker     loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
395*bebae9c0SAndroid Build Coastguard Worker     loc_outmix.outputMix = outputMixObject;
396*bebae9c0SAndroid Build Coastguard Worker     SLDataSink audioSnk;
397*bebae9c0SAndroid Build Coastguard Worker     audioSnk.pLocator = &loc_outmix;
398*bebae9c0SAndroid Build Coastguard Worker     audioSnk.pFormat = NULL;
399*bebae9c0SAndroid Build Coastguard Worker 
400*bebae9c0SAndroid Build Coastguard Worker     // create audio player
401*bebae9c0SAndroid Build Coastguard Worker     SLInterfaceID ids2[3] = {SL_IID_BUFFERQUEUE, SL_IID_PLAYBACKRATE, SL_IID_EFFECTSEND};
402*bebae9c0SAndroid Build Coastguard Worker     SLboolean req2[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
403*bebae9c0SAndroid Build Coastguard Worker     SLObjectItf playerObject;
404*bebae9c0SAndroid Build Coastguard Worker     result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audioSrc,
405*bebae9c0SAndroid Build Coastguard Worker             &audioSnk, enableReverb ? 3 : (enablePlaybackRate ? 2 : 1), ids2, req2);
406*bebae9c0SAndroid Build Coastguard Worker     if (SL_RESULT_SUCCESS != result) {
407*bebae9c0SAndroid Build Coastguard Worker         fprintf(stderr, "can't create audio player\n");
408*bebae9c0SAndroid Build Coastguard Worker         goto no_player;
409*bebae9c0SAndroid Build Coastguard Worker     }
410*bebae9c0SAndroid Build Coastguard Worker 
411*bebae9c0SAndroid Build Coastguard Worker     {
412*bebae9c0SAndroid Build Coastguard Worker 
413*bebae9c0SAndroid Build Coastguard Worker     // realize the player
414*bebae9c0SAndroid Build Coastguard Worker     result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
415*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
416*bebae9c0SAndroid Build Coastguard Worker 
417*bebae9c0SAndroid Build Coastguard Worker     // get the effect send interface and enable effect send reverb for this player
418*bebae9c0SAndroid Build Coastguard Worker     if (enableReverb) {
419*bebae9c0SAndroid Build Coastguard Worker         SLEffectSendItf playerEffectSend;
420*bebae9c0SAndroid Build Coastguard Worker         result = (*playerObject)->GetInterface(playerObject, SL_IID_EFFECTSEND, &playerEffectSend);
421*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
422*bebae9c0SAndroid Build Coastguard Worker         result = (*playerEffectSend)->EnableEffectSend(playerEffectSend, mixEnvironmentalReverb,
423*bebae9c0SAndroid Build Coastguard Worker                 SL_BOOLEAN_TRUE, (SLmillibel) 0);
424*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
425*bebae9c0SAndroid Build Coastguard Worker     }
426*bebae9c0SAndroid Build Coastguard Worker 
427*bebae9c0SAndroid Build Coastguard Worker     // get the playback rate interface and configure the rate
428*bebae9c0SAndroid Build Coastguard Worker     SLPlaybackRateItf playerPlaybackRate;
429*bebae9c0SAndroid Build Coastguard Worker     SLpermille currentRate = 0;
430*bebae9c0SAndroid Build Coastguard Worker     if (enablePlaybackRate) {
431*bebae9c0SAndroid Build Coastguard Worker         result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAYBACKRATE,
432*bebae9c0SAndroid Build Coastguard Worker                 &playerPlaybackRate);
433*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
434*bebae9c0SAndroid Build Coastguard Worker         SLpermille defaultRate;
435*bebae9c0SAndroid Build Coastguard Worker         result = (*playerPlaybackRate)->GetRate(playerPlaybackRate, &defaultRate);
436*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
437*bebae9c0SAndroid Build Coastguard Worker         SLuint32 defaultProperties;
438*bebae9c0SAndroid Build Coastguard Worker         result = (*playerPlaybackRate)->GetProperties(playerPlaybackRate, &defaultProperties);
439*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
440*bebae9c0SAndroid Build Coastguard Worker         printf("default playback rate %d per mille, properties 0x%x\n", defaultRate,
441*bebae9c0SAndroid Build Coastguard Worker                 defaultProperties);
442*bebae9c0SAndroid Build Coastguard Worker         if (initialRate <= 0) {
443*bebae9c0SAndroid Build Coastguard Worker             initialRate = defaultRate;
444*bebae9c0SAndroid Build Coastguard Worker         }
445*bebae9c0SAndroid Build Coastguard Worker         if (finalRate <= 0) {
446*bebae9c0SAndroid Build Coastguard Worker             finalRate = initialRate;
447*bebae9c0SAndroid Build Coastguard Worker         }
448*bebae9c0SAndroid Build Coastguard Worker         currentRate = defaultRate;
449*bebae9c0SAndroid Build Coastguard Worker         if (finalRate == initialRate) {
450*bebae9c0SAndroid Build Coastguard Worker             deltaRate = 0;
451*bebae9c0SAndroid Build Coastguard Worker         } else if (finalRate < initialRate) {
452*bebae9c0SAndroid Build Coastguard Worker             deltaRate = -deltaRate;
453*bebae9c0SAndroid Build Coastguard Worker         }
454*bebae9c0SAndroid Build Coastguard Worker         if (initialRate != defaultRate) {
455*bebae9c0SAndroid Build Coastguard Worker             result = (*playerPlaybackRate)->SetRate(playerPlaybackRate, initialRate);
456*bebae9c0SAndroid Build Coastguard Worker             if (SL_RESULT_FEATURE_UNSUPPORTED == result) {
457*bebae9c0SAndroid Build Coastguard Worker                 fprintf(stderr, "initial playback rate %d is unsupported\n", initialRate);
458*bebae9c0SAndroid Build Coastguard Worker                 deltaRate = 0;
459*bebae9c0SAndroid Build Coastguard Worker             } else if (SL_RESULT_PARAMETER_INVALID == result) {
460*bebae9c0SAndroid Build Coastguard Worker                 fprintf(stderr, "initial playback rate %d is invalid\n", initialRate);
461*bebae9c0SAndroid Build Coastguard Worker                 deltaRate = 0;
462*bebae9c0SAndroid Build Coastguard Worker             } else {
463*bebae9c0SAndroid Build Coastguard Worker                 assert(SL_RESULT_SUCCESS == result);
464*bebae9c0SAndroid Build Coastguard Worker                 currentRate = initialRate;
465*bebae9c0SAndroid Build Coastguard Worker             }
466*bebae9c0SAndroid Build Coastguard Worker         }
467*bebae9c0SAndroid Build Coastguard Worker     }
468*bebae9c0SAndroid Build Coastguard Worker 
469*bebae9c0SAndroid Build Coastguard Worker     // get the play interface
470*bebae9c0SAndroid Build Coastguard Worker     SLPlayItf playerPlay;
471*bebae9c0SAndroid Build Coastguard Worker     result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);
472*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
473*bebae9c0SAndroid Build Coastguard Worker 
474*bebae9c0SAndroid Build Coastguard Worker     // get the buffer queue interface
475*bebae9c0SAndroid Build Coastguard Worker     SLBufferQueueItf playerBufferQueue;
476*bebae9c0SAndroid Build Coastguard Worker     result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE,
477*bebae9c0SAndroid Build Coastguard Worker             &playerBufferQueue);
478*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
479*bebae9c0SAndroid Build Coastguard Worker 
480*bebae9c0SAndroid Build Coastguard Worker     // loop until EOF or no more buffers
481*bebae9c0SAndroid Build Coastguard Worker     for (which = 0; which < numBuffers; ++which) {
482*bebae9c0SAndroid Build Coastguard Worker         void *buffer = (char *)buffers + framesPerBuffer * sfframesize * which;
483*bebae9c0SAndroid Build Coastguard Worker         sf_count_t frames = framesPerBuffer;
484*bebae9c0SAndroid Build Coastguard Worker         sf_count_t count;
485*bebae9c0SAndroid Build Coastguard Worker         switch (transferFormat) {
486*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_FLOAT:
487*bebae9c0SAndroid Build Coastguard Worker             count = sf_readf_float(sndfile, (float *) buffer, frames);
488*bebae9c0SAndroid Build Coastguard Worker             break;
489*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_32_BIT:
490*bebae9c0SAndroid Build Coastguard Worker             count = sf_readf_int(sndfile, (int *) buffer, frames);
491*bebae9c0SAndroid Build Coastguard Worker             break;
492*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_24_BIT_PACKED:
493*bebae9c0SAndroid Build Coastguard Worker             count = sf_readf_int(sndfile, (int *) buffer, frames);
494*bebae9c0SAndroid Build Coastguard Worker             break;
495*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_16_BIT:
496*bebae9c0SAndroid Build Coastguard Worker         case AUDIO_FORMAT_PCM_8_BIT:
497*bebae9c0SAndroid Build Coastguard Worker             count = sf_readf_short(sndfile, (short *) buffer, frames);
498*bebae9c0SAndroid Build Coastguard Worker             break;
499*bebae9c0SAndroid Build Coastguard Worker         default:
500*bebae9c0SAndroid Build Coastguard Worker             count = 0;
501*bebae9c0SAndroid Build Coastguard Worker             break;
502*bebae9c0SAndroid Build Coastguard Worker         }
503*bebae9c0SAndroid Build Coastguard Worker         if (0 >= count) {
504*bebae9c0SAndroid Build Coastguard Worker             eof = SL_BOOLEAN_TRUE;
505*bebae9c0SAndroid Build Coastguard Worker             break;
506*bebae9c0SAndroid Build Coastguard Worker         }
507*bebae9c0SAndroid Build Coastguard Worker 
508*bebae9c0SAndroid Build Coastguard Worker         // enqueue a buffer
509*bebae9c0SAndroid Build Coastguard Worker         SLuint32 nbytes = count * sfframesize;
510*bebae9c0SAndroid Build Coastguard Worker         nbytes = squeeze(buffer, nbytes);
511*bebae9c0SAndroid Build Coastguard Worker         result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, nbytes);
512*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
513*bebae9c0SAndroid Build Coastguard Worker     }
514*bebae9c0SAndroid Build Coastguard Worker     if (which >= numBuffers) {
515*bebae9c0SAndroid Build Coastguard Worker         which = 0;
516*bebae9c0SAndroid Build Coastguard Worker     }
517*bebae9c0SAndroid Build Coastguard Worker 
518*bebae9c0SAndroid Build Coastguard Worker     // register a callback on the buffer queue
519*bebae9c0SAndroid Build Coastguard Worker     result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, callback, NULL);
520*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
521*bebae9c0SAndroid Build Coastguard Worker 
522*bebae9c0SAndroid Build Coastguard Worker #define FIFO_FRAMES 16384
523*bebae9c0SAndroid Build Coastguard Worker     void *fifoBuffer = malloc(FIFO_FRAMES * sfframesize);
524*bebae9c0SAndroid Build Coastguard Worker     fifo = new audio_utils_fifo(FIFO_FRAMES, sfframesize, fifoBuffer);
525*bebae9c0SAndroid Build Coastguard Worker     fifoReader = new audio_utils_fifo_reader(*fifo, true /*throttlesWriter*/);
526*bebae9c0SAndroid Build Coastguard Worker     fifoWriter = new audio_utils_fifo_writer(*fifo);
527*bebae9c0SAndroid Build Coastguard Worker 
528*bebae9c0SAndroid Build Coastguard Worker     // create thread to read from file
529*bebae9c0SAndroid Build Coastguard Worker     pthread_t thread;
530*bebae9c0SAndroid Build Coastguard Worker     int ok = pthread_create(&thread, (const pthread_attr_t *) NULL, file_reader_loop, NULL);
531*bebae9c0SAndroid Build Coastguard Worker     assert(0 == ok);
532*bebae9c0SAndroid Build Coastguard Worker 
533*bebae9c0SAndroid Build Coastguard Worker     // give thread a head start so that the pipe is initially filled
534*bebae9c0SAndroid Build Coastguard Worker     sleep(1);
535*bebae9c0SAndroid Build Coastguard Worker 
536*bebae9c0SAndroid Build Coastguard Worker     // set the player's state to playing
537*bebae9c0SAndroid Build Coastguard Worker     result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
538*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
539*bebae9c0SAndroid Build Coastguard Worker 
540*bebae9c0SAndroid Build Coastguard Worker     // get the initial time
541*bebae9c0SAndroid Build Coastguard Worker     struct timespec prevTs;
542*bebae9c0SAndroid Build Coastguard Worker     clock_gettime(CLOCK_MONOTONIC, &prevTs);
543*bebae9c0SAndroid Build Coastguard Worker     long elapsedNs = 0;
544*bebae9c0SAndroid Build Coastguard Worker     long deltaRateNs = deltaRateMs * 1000000;
545*bebae9c0SAndroid Build Coastguard Worker 
546*bebae9c0SAndroid Build Coastguard Worker     // wait until the buffer queue is empty
547*bebae9c0SAndroid Build Coastguard Worker     SLBufferQueueState bufqstate;
548*bebae9c0SAndroid Build Coastguard Worker     for (;;) {
549*bebae9c0SAndroid Build Coastguard Worker         result = (*playerBufferQueue)->GetState(playerBufferQueue, &bufqstate);
550*bebae9c0SAndroid Build Coastguard Worker         assert(SL_RESULT_SUCCESS == result);
551*bebae9c0SAndroid Build Coastguard Worker         if (0 >= bufqstate.count) {
552*bebae9c0SAndroid Build Coastguard Worker             break;
553*bebae9c0SAndroid Build Coastguard Worker         }
554*bebae9c0SAndroid Build Coastguard Worker         if (!enablePlaybackRate || deltaRate == 0) {
555*bebae9c0SAndroid Build Coastguard Worker             sleep(1);
556*bebae9c0SAndroid Build Coastguard Worker         } else {
557*bebae9c0SAndroid Build Coastguard Worker             struct timespec curTs;
558*bebae9c0SAndroid Build Coastguard Worker             clock_gettime(CLOCK_MONOTONIC, &curTs);
559*bebae9c0SAndroid Build Coastguard Worker             elapsedNs += (curTs.tv_sec - prevTs.tv_sec) * 1000000000 +
560*bebae9c0SAndroid Build Coastguard Worker                     // this term can be negative
561*bebae9c0SAndroid Build Coastguard Worker                     (curTs.tv_nsec - prevTs.tv_nsec);
562*bebae9c0SAndroid Build Coastguard Worker             prevTs = curTs;
563*bebae9c0SAndroid Build Coastguard Worker             if (elapsedNs < deltaRateNs) {
564*bebae9c0SAndroid Build Coastguard Worker                 usleep((deltaRateNs - elapsedNs) / 1000);
565*bebae9c0SAndroid Build Coastguard Worker                 continue;
566*bebae9c0SAndroid Build Coastguard Worker             }
567*bebae9c0SAndroid Build Coastguard Worker             elapsedNs -= deltaRateNs;
568*bebae9c0SAndroid Build Coastguard Worker             SLpermille nextRate = currentRate + deltaRate;
569*bebae9c0SAndroid Build Coastguard Worker             result = (*playerPlaybackRate)->SetRate(playerPlaybackRate, nextRate);
570*bebae9c0SAndroid Build Coastguard Worker             if (SL_RESULT_SUCCESS != result) {
571*bebae9c0SAndroid Build Coastguard Worker                 fprintf(stderr, "next playback rate %d is unsupported\n", nextRate);
572*bebae9c0SAndroid Build Coastguard Worker             } else if (SL_RESULT_PARAMETER_INVALID == result) {
573*bebae9c0SAndroid Build Coastguard Worker                 fprintf(stderr, "next playback rate %d is invalid\n", nextRate);
574*bebae9c0SAndroid Build Coastguard Worker             } else {
575*bebae9c0SAndroid Build Coastguard Worker                 assert(SL_RESULT_SUCCESS == result);
576*bebae9c0SAndroid Build Coastguard Worker             }
577*bebae9c0SAndroid Build Coastguard Worker             currentRate = nextRate;
578*bebae9c0SAndroid Build Coastguard Worker             if (currentRate >= max(initialRate, finalRate)) {
579*bebae9c0SAndroid Build Coastguard Worker                 currentRate = max(initialRate, finalRate);
580*bebae9c0SAndroid Build Coastguard Worker                 deltaRate = -abs(deltaRate);
581*bebae9c0SAndroid Build Coastguard Worker             } else if (currentRate <= min(initialRate, finalRate)) {
582*bebae9c0SAndroid Build Coastguard Worker                 currentRate = min(initialRate, finalRate);
583*bebae9c0SAndroid Build Coastguard Worker                 deltaRate = abs(deltaRate);
584*bebae9c0SAndroid Build Coastguard Worker             }
585*bebae9c0SAndroid Build Coastguard Worker         }
586*bebae9c0SAndroid Build Coastguard Worker 
587*bebae9c0SAndroid Build Coastguard Worker     }
588*bebae9c0SAndroid Build Coastguard Worker 
589*bebae9c0SAndroid Build Coastguard Worker     // wait for reader thread to exit
590*bebae9c0SAndroid Build Coastguard Worker     ok = pthread_join(thread, (void **) NULL);
591*bebae9c0SAndroid Build Coastguard Worker     assert(0 == ok);
592*bebae9c0SAndroid Build Coastguard Worker 
593*bebae9c0SAndroid Build Coastguard Worker     // set the player's state to stopped
594*bebae9c0SAndroid Build Coastguard Worker     result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_STOPPED);
595*bebae9c0SAndroid Build Coastguard Worker     assert(SL_RESULT_SUCCESS == result);
596*bebae9c0SAndroid Build Coastguard Worker 
597*bebae9c0SAndroid Build Coastguard Worker     // destroy audio player
598*bebae9c0SAndroid Build Coastguard Worker     (*playerObject)->Destroy(playerObject);
599*bebae9c0SAndroid Build Coastguard Worker 
600*bebae9c0SAndroid Build Coastguard Worker     delete fifoWriter;
601*bebae9c0SAndroid Build Coastguard Worker     fifoWriter = NULL;
602*bebae9c0SAndroid Build Coastguard Worker     delete fifoReader;
603*bebae9c0SAndroid Build Coastguard Worker     fifoReader = NULL;
604*bebae9c0SAndroid Build Coastguard Worker     delete fifo;
605*bebae9c0SAndroid Build Coastguard Worker     fifo = NULL;
606*bebae9c0SAndroid Build Coastguard Worker     free(fifoBuffer);
607*bebae9c0SAndroid Build Coastguard Worker     fifoBuffer = NULL;
608*bebae9c0SAndroid Build Coastguard Worker 
609*bebae9c0SAndroid Build Coastguard Worker     }
610*bebae9c0SAndroid Build Coastguard Worker 
611*bebae9c0SAndroid Build Coastguard Worker no_player:
612*bebae9c0SAndroid Build Coastguard Worker 
613*bebae9c0SAndroid Build Coastguard Worker     // destroy output mix
614*bebae9c0SAndroid Build Coastguard Worker     (*outputMixObject)->Destroy(outputMixObject);
615*bebae9c0SAndroid Build Coastguard Worker 
616*bebae9c0SAndroid Build Coastguard Worker     // destroy engine
617*bebae9c0SAndroid Build Coastguard Worker     (*engineObject)->Destroy(engineObject);
618*bebae9c0SAndroid Build Coastguard Worker 
619*bebae9c0SAndroid Build Coastguard Worker     }
620*bebae9c0SAndroid Build Coastguard Worker 
621*bebae9c0SAndroid Build Coastguard Worker close_sndfile:
622*bebae9c0SAndroid Build Coastguard Worker 
623*bebae9c0SAndroid Build Coastguard Worker     (void) sf_close(sndfile);
624*bebae9c0SAndroid Build Coastguard Worker 
625*bebae9c0SAndroid Build Coastguard Worker     return EXIT_SUCCESS;
626*bebae9c0SAndroid Build Coastguard Worker }
627