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 // multiplay is a command-line test app that plays multiple files randomly
18 
19 #include <SLES/OpenSLES.h>
20 #include <assert.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 
26 // Describes the state of one player
27 
28 typedef struct {
29     SLObjectItf mPlayerObject;
30     SLPlayItf mPlayerPlay;
31     SLSeekItf mPlayerSeek;
32     SLPrefetchStatusItf mPlayerPrefetchStatus;
33     SLVolumeItf mPlayerVolume;
34     SLmillisecond mPlayerDuration;
35     SLboolean mPlayerErrorInCallback;
36     SLboolean mPlayerErrorReported;
37 } Player;
38 
39 // Strings corresponding to result codes; FIXME should move to a common test library
40 
41 static const char *result_strings[] = {
42     "SUCCESS",
43     "PRECONDITIONS_VIOLATED",
44     "PARAMETER_INVALID",
45     "MEMORY_FAILURE",
46     "RESOURCE_ERROR",
47     "RESOURCE_LOST",
48     "IO_ERROR",
49     "BUFFER_INSUFFICIENT",
50     "CONTENT_CORRUPTED",
51     "CONTENT_UNSUPPORTED",
52     "CONTENT_NOT_FOUND",
53     "PERMISSION_DENIED",
54     "FEATURE_UNSUPPORTED",
55     "INTERNAL_ERROR",
56     "UNKNOWN_ERROR",
57     "OPERATION_ABORTED",
58     "CONTROL_LOST"
59 };
60 
61 // Convert result to string; FIXME should move to common test library
62 
result_to_string(SLresult result)63 static const char *result_to_string(SLresult result)
64 {
65     static char buffer[32];
66     if ( /* result >= 0 && */ result < sizeof(result_strings) / sizeof(result_strings[0]))
67         return result_strings[result];
68     sprintf(buffer, "%d", (int) result);
69     return buffer;
70 }
71 
72 // Compare result against expected and exit suddenly if wrong
73 
check2(SLresult result,int line)74 void check2(SLresult result, int line)
75 {
76     if (SL_RESULT_SUCCESS != result) {
77         fprintf(stderr, "error %s at line %d\n", result_to_string(result), line);
78         exit(EXIT_FAILURE);
79     }
80 }
81 
82 // Same as above but automatically adds the source code line number
83 
84 #define check(result) check2(result, __LINE__)
85 
86 // Prefetch status callback
87 
88 #define PREFETCHEVENT_ERROR_CANDIDATE \
89             (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE)
90 
prefetch_callback(SLPrefetchStatusItf caller,void * context,SLuint32 event)91 void prefetch_callback(SLPrefetchStatusItf caller, void *context, SLuint32 event)
92 {
93     SLresult result;
94     assert(context != NULL);
95     Player *p = (Player *) context;
96     assert(p->mPlayerPrefetchStatus == caller);
97     SLpermille level;
98     result = (*caller)->GetFillLevel(caller, &level);
99     check(result);
100     SLuint32 status;
101     result = (*caller)->GetPrefetchStatus(caller, &status);
102     check(result);
103     //fprintf(stderr, "PrefetchEventCallback: received event %u, level %u, status %u\n",
104     //      event, level, status);
105     if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE))
106             && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
107         p->mPlayerErrorInCallback = SL_BOOLEAN_TRUE;
108     }
109 }
110 
111 // Main program
112 
main(int argc,char ** argv)113 int main(int argc, char **argv)
114 {
115     int i;
116     const char *arg;
117     int numPlayers = 0;
118     int playTimeInMilliseconds = 0; // default to run forever
119     SLmillibel mixVolumeLevel = 0;
120     for (i = 1; i < argc; ++i) {
121         arg = argv[i];
122         if (arg[0] != '-')
123             break;
124         if (!strncmp(arg, "-n", 2))
125             numPlayers = atoi(&arg[2]);
126         else if (!strncmp(arg, "-v", 2))
127             mixVolumeLevel = atoi(&arg[2]);
128         else if (!strncmp(arg, "-t", 2))
129             playTimeInMilliseconds = atoi(&arg[2]) * 1000;
130         else
131             fprintf(stderr, "unknown option: %s\n", arg);
132     }
133     int numPathnames = argc - i;
134     if (numPathnames <= 0) {
135         fprintf(stderr, "usage: %s file.wav ...\n", argv[0]);
136         return EXIT_FAILURE;
137     }
138     if (numPlayers <= 0) {
139         numPlayers = numPathnames;
140     }
141     Player *players = (Player *) calloc(numPlayers, sizeof(Player));
142     assert(NULL != players);
143     char **pathnames = &argv[i];
144     SLresult result;
145 
146     // engine
147     const SLInterfaceID engine_ids[] = {SL_IID_ENGINE};
148     const SLboolean engine_req[] = {SL_BOOLEAN_TRUE};
149     SLObjectItf engineObject;
150     result = slCreateEngine(&engineObject, 0, NULL, 1, engine_ids, engine_req);
151     check(result);
152     result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
153     check(result);
154     SLEngineItf engineEngine;
155     result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
156     check(result);
157 
158     // mixer
159     const SLInterfaceID mix_ids[] = {SL_IID_VOLUME};
160     const SLboolean mix_req[] = {SL_BOOLEAN_TRUE};
161     SLObjectItf mixObject;
162     result = (*engineEngine)->CreateOutputMix(engineEngine, &mixObject, 0, mix_ids, mix_req);
163     check(result);
164     result = (*mixObject)->Realize(mixObject, SL_BOOLEAN_FALSE);
165     check(result);
166 #if 0
167     SLVolumeItf mixVolume;
168     result = (*mixObject)->GetInterface(mixObject, SL_IID_VOLUME, &mixVolume);
169     check(result);
170     SLmillibel mixVolumeLevelDefault;
171     result = (*mixVolume)->GetVolumeLevel(mixVolume, &mixVolumeLevelDefault);
172     check(result);
173     printf("default mix volume level = %d\n", mixVolumeLevelDefault);
174 #endif
175 
176     printf("numPathnames=%d\n", numPathnames);
177     printf("numPlayers=%d\n", numPlayers);
178     Player *p;
179 
180     // create all the players
181     for (i = 0; i < numPlayers; ++i) {
182         const SLInterfaceID player_ids[] =
183                 {SL_IID_PLAY, SL_IID_VOLUME, SL_IID_SEEK, SL_IID_PREFETCHSTATUS};
184         const SLboolean player_req[] =
185                 {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
186         p = &players[i];
187         SLDataLocator_URI locURI = {SL_DATALOCATOR_URI, (SLchar *) pathnames[i % numPathnames]};
188         SLDataFormat_MIME dfMIME = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
189         SLDataSource audioSrc = {&locURI, &dfMIME};
190         SLDataLocator_OutputMix locOutputMix = {SL_DATALOCATOR_OUTPUTMIX, mixObject};
191         SLDataSink audioSnk = {&locOutputMix, NULL};
192         result = (*engineEngine)->CreateAudioPlayer(engineEngine, &p->mPlayerObject, &audioSrc,
193             &audioSnk, sizeof(player_ids)/sizeof(player_ids[0]), player_ids, player_req);
194         check(result);
195         result = (*p->mPlayerObject)->Realize(p->mPlayerObject, SL_BOOLEAN_FALSE);
196         check(result);
197         result = (*p->mPlayerObject)->GetInterface(p->mPlayerObject, SL_IID_PLAY, &p->mPlayerPlay);
198         check(result);
199         result = (*p->mPlayerObject)->GetInterface(p->mPlayerObject, SL_IID_VOLUME,
200             &p->mPlayerVolume);
201         check(result);
202         result = (*p->mPlayerObject)->GetInterface(p->mPlayerObject, SL_IID_SEEK, &p->mPlayerSeek);
203         check(result);
204         result = (*p->mPlayerObject)->GetInterface(p->mPlayerObject, SL_IID_PREFETCHSTATUS,
205                 &p->mPlayerPrefetchStatus);
206         check(result);
207         result = (*p->mPlayerPrefetchStatus)->RegisterCallback(p->mPlayerPrefetchStatus,
208                 prefetch_callback, p);
209         check(result);
210         result = (*p->mPlayerPrefetchStatus)->SetCallbackEventsMask(p->mPlayerPrefetchStatus,
211                 SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE);
212         check(result);
213     }
214 
215     // now loop randomly doing things to the players
216     for (;;) {
217         SLmillisecond delay = 100 + (rand() & 1023);
218         printf("sleep %u\n", (unsigned) delay);
219         usleep(delay * 1000);
220         i = (rand() & 0x7FFFFFFF) % numPlayers;
221         p = &players[i];
222         if (p->mPlayerErrorReported)
223             continue;
224         printf("player %d (%s): ", i, pathnames[i]);
225         if (p->mPlayerErrorInCallback && !p->mPlayerErrorReported) {
226             printf("error, ");
227             p->mPlayerErrorReported = SL_BOOLEAN_TRUE;
228         }
229         result = (*p->mPlayerPlay)->GetDuration(p->mPlayerPlay, &p->mPlayerDuration);
230         check(result);
231         if (p->mPlayerDuration == SL_TIME_UNKNOWN) {
232             printf("duration unknown, ");
233         } else {
234             printf("duration %d ms, ", (int) p->mPlayerDuration);
235         }
236         SLuint32 state;
237         result = (*p->mPlayerPlay)->GetPlayState(p->mPlayerPlay, &state);
238         check(result);
239         printf("state = ");
240         switch (state) {
241         case SL_PLAYSTATE_STOPPED:
242             printf("STOPPED");
243             break;
244         case SL_PLAYSTATE_PAUSED:
245             printf("PAUSED");
246             break;
247         case SL_PLAYSTATE_PLAYING:
248             printf("PLAYING");
249             break;
250         default:
251             printf("%u", (unsigned) state);
252             break;
253         }
254         printf("\n");
255         if (state == SL_PLAYSTATE_STOPPED || state == SL_PLAYSTATE_PAUSED) {
256             SLmillibel volumeLevel = -((rand() & 0x7FFFFFFF) % ((SL_MILLIBEL_MIN + 1) / 10));
257             printf("volume %d\n", volumeLevel);
258             result = (*p->mPlayerVolume)->SetVolumeLevel(p->mPlayerVolume, volumeLevel);
259             check(result);
260             result = (*p->mPlayerVolume)->EnableStereoPosition(p->mPlayerVolume, SL_BOOLEAN_TRUE);
261             check(result);
262             SLpermille stereoPosition = ((rand() & 0x7FFFFFFF) % 2001) - 1000;
263             printf("position %d\n", stereoPosition);
264             result = (*p->mPlayerVolume)->SetStereoPosition(p->mPlayerVolume, stereoPosition);
265             check(result);
266             if (state != SL_PLAYSTATE_STOPPED) {
267                 result = (*p->mPlayerSeek)->SetPosition(p->mPlayerSeek, 0, SL_SEEKMODE_FAST);
268                 check(result);
269             }
270             result = (*p->mPlayerPlay)->SetPlayState(p->mPlayerPlay, SL_PLAYSTATE_PLAYING);
271             check(result);
272         }
273         if ((playTimeInMilliseconds > 0) && ((playTimeInMilliseconds -= delay) < 0))
274             break;
275     }
276 
277     for (i = 0; i < numPlayers; ++i) {
278         SLObjectItf playerObject = players[i].mPlayerObject;
279         (*playerObject)->Destroy(playerObject);
280     }
281     (*mixObject)->Destroy(mixObject);
282     (*engineObject)->Destroy(engineObject);
283 
284     return EXIT_SUCCESS;
285 }
286