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
28typedef 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
41static 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
63static 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
74void 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
91void 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
113int 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