1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 
21 #include <SLES/OpenSLES.h>
22 
23 
24 #define MAX_NUMBER_INTERFACES 3
25 
26 
27 //-----------------------------------------------------------------
28 /* Exits the application if an error is encountered */
ExitOnError(SLresult result)29 void ExitOnError( SLresult result )
30 {
31     if (SL_RESULT_SUCCESS != result) {
32         fprintf(stdout, "%u error code encountered, exiting\n", result);
33         exit(EXIT_FAILURE);
34     }
35 }
36 
37 //-----------------------------------------------------------------
38 /* PlayItf callback for an audio player */
PlayEventCallback(SLPlayItf caller __unused,void * pContext __unused,SLuint32 event)39 void PlayEventCallback( SLPlayItf caller __unused,  void *pContext __unused, SLuint32 event)
40 {
41     fprintf(stdout, "PlayEventCallback event = ");
42     if (event & SL_PLAYEVENT_HEADATEND) {
43         fprintf(stdout, "SL_PLAYEVENT_HEADATEND ");
44     }
45     if (event & SL_PLAYEVENT_HEADATMARKER) {
46         fprintf(stdout, "SL_PLAYEVENT_HEADATMARKER ");
47     }
48     if (event & SL_PLAYEVENT_HEADATNEWPOS) {
49         fprintf(stdout, "SL_PLAYEVENT_HEADATNEWPOS ");
50     }
51     if (event & SL_PLAYEVENT_HEADMOVING) {
52         fprintf(stdout, "SL_PLAYEVENT_HEADMOVING ");
53     }
54     if (event & SL_PLAYEVENT_HEADSTALLED) {
55         fprintf(stdout, "SL_PLAYEVENT_HEADSTALLED");
56     }
57     fprintf(stdout, "\n");
58 }
59 
60 //-----------------------------------------------------------------
61 
62 /* Play two audio URIs, pan them left and right  */
TestPlayUri(SLObjectItf sl,const char * path,const char * path2)63 void TestPlayUri( SLObjectItf sl, const char* path, const char* path2)
64 {
65     SLresult  result;
66     SLEngineItf EngineItf;
67 
68     /* Objects this application uses: two players and an ouput mix */
69     SLObjectItf  player, player2, outputMix;
70 
71     /* Source of audio data to play, we'll reuse the same source for two different players */
72     SLDataSource      audioSource;
73     SLDataLocator_URI uri;
74     SLDataFormat_MIME mime;
75 
76     /* Data sinks for the two audio players */
77     SLDataSink               audioSink;
78     SLDataLocator_OutputMix  locator_outputmix;
79 
80     /* Play, Volume and PrefetchStatus interfaces for the audio players */
81     SLPlayItf           playItf, playItf2;
82     SLVolumeItf         volItf, volItf2;
83     SLPrefetchStatusItf prefetchItf, prefetchItf2;
84 
85     SLboolean required[MAX_NUMBER_INTERFACES];
86     SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];
87 
88     /* Get the SL Engine Interface which is implicit */
89     result = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf);
90     ExitOnError(result);
91 
92     /* Initialize arrays required[] and iidArray[] */
93     for (int i=0 ; i < MAX_NUMBER_INTERFACES ; i++) {
94         required[i] = SL_BOOLEAN_FALSE;
95         iidArray[i] = SL_IID_NULL;
96     }
97     /* Set arrays required[] and iidArray[] for SLVolumeItf and SLPrefetchStatusItf interfaces */
98     /*  (SLPlayItf is implicit) */
99     required[0] = SL_BOOLEAN_TRUE;
100     iidArray[0] = SL_IID_VOLUME;
101     required[1] = SL_BOOLEAN_TRUE;
102     iidArray[1] = SL_IID_PREFETCHSTATUS;
103 
104     /* ------------------------------------------------------ */
105     /* Configuration of the output mix  */
106 
107     /* Create Output Mix object to be used each player */
108      result = (*EngineItf)->CreateOutputMix(EngineItf, &outputMix, 0, iidArray, required);
109      ExitOnError(result);
110 
111     /* Realize the Output Mix object in synchronous mode */
112     result = (*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE);
113     ExitOnError(result);
114 
115     /* Setup the data sink structure */
116     locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
117     locator_outputmix.outputMix   = outputMix;
118     audioSink.pLocator            = (void *)&locator_outputmix;
119     audioSink.pFormat             = NULL;
120 
121     /* ------------------------------------------------------ */
122     /* Configuration of the players  */
123 
124     /* Setup the data source structure for the first URI */
125     uri.locatorType = SL_DATALOCATOR_URI;
126     uri.URI         =  (SLchar*) path;
127     mime.formatType    = SL_DATAFORMAT_MIME;
128     /*     this is how ignored mime information is specified, according to OpenSL ES spec
129      *     in 9.1.6 SLDataFormat_MIME and 8.23 SLMetadataTraversalItf GetChildInfo */
130     mime.mimeType      = (SLchar*)NULL;
131     mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
132 
133     audioSource.pFormat      = (void *)&mime;
134     audioSource.pLocator     = (void *)&uri;
135 
136     /* Create the first audio player */
137     result = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, 2,
138             iidArray, required);
139     ExitOnError(result);
140 
141     /* Create the second audio player with a different path for its data source */
142     uri.URI =  (SLchar*) path2;
143     audioSource.pLocator = (void *)&uri;
144     result = (*EngineItf)->CreateAudioPlayer(EngineItf, &player2, &audioSource, &audioSink, 2,
145             iidArray, required);
146     ExitOnError(result);
147 
148     /* Realize the players in synchronous mode. */
149     result = (*player)->Realize(player, SL_BOOLEAN_FALSE); ExitOnError(result);
150     result = (*player)->Realize(player2, SL_BOOLEAN_FALSE); ExitOnError(result);
151     //fprintf(stdout, "URI example: after Realize\n");
152 
153     /* Get the SLPlayItf, SLVolumeItf and SLPrefetchStatusItf interfaces for each player */
154     result = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf);
155     ExitOnError(result);
156     result = (*player)->GetInterface(player2, SL_IID_PLAY, (void*)&playItf2);
157     ExitOnError(result);
158 
159     result = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volItf);
160     ExitOnError(result);
161     result = (*player2)->GetInterface(player2, SL_IID_VOLUME, (void*)&volItf2);
162     ExitOnError(result);
163 
164     result = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf);
165     ExitOnError(result);
166     result = (*player2)->GetInterface(player2, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf2);
167     ExitOnError(result);
168 
169     /*  Setup to receive playback events */
170     result = (*playItf)->RegisterCallback(playItf, PlayEventCallback, &playItf);
171     ExitOnError(result);
172     result = (*playItf)->SetCallbackEventsMask(playItf,
173             SL_PLAYEVENT_HEADATEND| SL_PLAYEVENT_HEADATMARKER | SL_PLAYEVENT_HEADATNEWPOS
174             | SL_PLAYEVENT_HEADMOVING | SL_PLAYEVENT_HEADSTALLED);
175     ExitOnError(result);
176 
177     /* Set the player volume */
178     result = (*volItf)->SetVolumeLevel( volItf, -300);
179     ExitOnError(result);
180     /* Pan the first player to the left */
181     result = (*volItf)->EnableStereoPosition( volItf, SL_BOOLEAN_TRUE); ExitOnError(result);
182     result = (*volItf)->SetStereoPosition( volItf, -1000); ExitOnError(result);
183     /* Pan the second player to the right */
184     result = (*volItf2)->EnableStereoPosition( volItf2, SL_BOOLEAN_TRUE); ExitOnError(result);
185     result = (*volItf2)->SetStereoPosition( volItf2, 1000); ExitOnError(result);
186 
187     /* ------------------------------------------------------ */
188     /* Playback */
189 
190     /* Start the data prefetching by setting the players to the paused state */
191     result = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED );
192     ExitOnError(result);
193     result = (*playItf2)->SetPlayState( playItf2, SL_PLAYSTATE_PAUSED );
194     ExitOnError(result);
195 
196     /*     wait until there's data to play */
197     SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
198     while (prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) {
199         usleep(100 * 1000);
200         (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus);
201     }
202     prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
203     while (prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) {
204         usleep(100 * 1000);
205         (*prefetchItf2)->GetPrefetchStatus(prefetchItf2, &prefetchStatus);
206     }
207 
208     result = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING );
209     ExitOnError(result);
210 
211     /* Wait 2s before starting the second player */
212     usleep(2000 * 1000);
213     fprintf(stdout, "URI example: starting to play %s\n", path2);
214     result = (*playItf2)->SetPlayState( playItf2, SL_PLAYSTATE_PLAYING );
215     ExitOnError(result);
216 
217     /* Display duration */
218     SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
219     result = (*playItf)->GetDuration(playItf, &durationInMsec);
220     ExitOnError(result);
221     if (durationInMsec == SL_TIME_UNKNOWN) {
222         fprintf(stdout, "Content duration of first URI is unknown\n");
223     } else {
224         fprintf(stdout, "Content duration of first URI is %u ms\n", durationInMsec);
225     }
226 
227     /* Wait as long as the duration of the first URI + 2s before stopping */
228     if (durationInMsec == SL_TIME_UNKNOWN) {
229         durationInMsec = 5000; /* arbitrary time when duration is unknown */
230     }
231     usleep((durationInMsec + 2000) * 1000);
232 
233     /* Make sure player is stopped */
234     fprintf(stdout, "URI example: stopping playback\n");
235     result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
236     ExitOnError(result);
237     result = (*playItf2)->SetPlayState(playItf2, SL_PLAYSTATE_STOPPED);
238     ExitOnError(result);
239 
240     /* Destroy the players */
241     (*player)->Destroy(player);
242     (*player2)->Destroy(player2);
243 
244     /* Destroy Output Mix object */
245     (*outputMix)->Destroy(outputMix);
246 }
247 
248 //-----------------------------------------------------------------
main(int argc,char * const argv[])249 int main(int argc, char* const argv[])
250 {
251     SLresult    result;
252     SLObjectItf sl;
253 
254     fprintf(stdout, "OpenSL ES test %s: exercises SLPlayItf, SLVolumeItf (incl. stereo position) ",
255             argv[0]);
256     fprintf(stdout, "and AudioPlayer with SLDataLocator_URI source / OutputMix sink\n");
257     fprintf(stdout, "Plays two sounds (or twice the same) and pans them left and right.");
258     fprintf(stdout, "Stops after the end of the first + 2s\n");
259 
260     if (argc == 1) {
261         fprintf(stdout, "Usage: \n\t%s url1 url2 \n\t%s url\n", argv[0], argv[0]);
262         fprintf(stdout, "Example: \"%s /sdcard/my.mp3 http://blabla/my.wav\" ", argv[0]);
263         fprintf(stdout, "or \"%s file:///sdcard/my.mp3\"\n", argv[0]);
264         exit(EXIT_FAILURE);
265     }
266 
267     SLEngineOption EngineOption[] = {
268             {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}
269     };
270 
271     result = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL);
272     ExitOnError(result);
273 
274     /* Realizing the SL Engine in synchronous mode. */
275     result = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
276     ExitOnError(result);
277 
278     if (argc == 2) {
279         TestPlayUri(sl, argv[1], argv[1]);
280     } else if (argc == 3) {
281         TestPlayUri(sl, argv[1], argv[2]);
282     }
283 
284     /* Shutdown OpenSL ES */
285     (*sl)->Destroy(sl);
286 
287     return EXIT_SUCCESS;
288 }
289