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// single-threaded, single-player monkey test
18
19#include <SLES/OpenSLES.h>
20#include <assert.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26typedef enum {
27    STATE_UNCHANGED,
28    STATE_INITIAL,
29    STATE_NONEXISTENT,
30    STATE_CREATED,
31    STATE_REALIZED,
32    STATE_PAUSED,
33    STATE_PLAYING,
34    STATE_STOPPED,
35    STATE_ERROR,
36//    STATE_IDLE,         // after Stop, then sleep for 3 seconds
37    STATE_TERMINAL
38} State_t;
39
40typedef struct {
41    SLObjectItf mObject;
42    SLPlayItf mPlay;
43    SLSeekItf mSeek;
44} Player_t, *Player_pt;
45
46typedef State_t (*Action_pt)(Player_pt player);
47
48SLObjectItf engineObject;
49SLEngineItf engineEngine;
50SLObjectItf outputMixObject;
51int countTransitions = 0;
52int maxTransitions = 10;
53
54State_t actionPause(Player_pt p)
55{
56    assert(NULL != p->mPlay);
57    SLresult result = (*p->mPlay)->SetPlayState(p->mPlay, SL_PLAYSTATE_PAUSED);
58    assert(SL_RESULT_SUCCESS == result);
59    return STATE_PAUSED;
60}
61
62State_t actionPlay(Player_pt p)
63{
64    assert(NULL != p->mPlay);
65    SLresult result = (*p->mPlay)->SetPlayState(p->mPlay, SL_PLAYSTATE_PLAYING);
66    assert(SL_RESULT_SUCCESS == result);
67    return STATE_PLAYING;
68}
69
70State_t actionStop(Player_pt p)
71{
72    assert(NULL != p->mPlay);
73    SLresult result = (*p->mPlay)->SetPlayState(p->mPlay, SL_PLAYSTATE_STOPPED);
74    assert(SL_RESULT_SUCCESS == result);
75    return STATE_STOPPED;
76}
77
78State_t actionRewind(Player_pt p)
79{
80    assert(NULL != p->mSeek);
81    SLresult result = (*p->mSeek)->SetPosition(p->mSeek, (SLmillisecond) 0, SL_SEEKMODE_FAST);
82    assert(SL_RESULT_SUCCESS == result);
83    return STATE_UNCHANGED;
84}
85
86State_t actionDestroy(Player_pt p)
87{
88    assert(NULL != p->mObject);
89    (*p->mObject)->Destroy(p->mObject);
90    p->mObject = NULL;
91    p->mPlay = NULL;
92    p->mSeek = NULL;
93    return STATE_NONEXISTENT;
94}
95
96State_t actionCreate(Player_pt p)
97{
98    // configure audio source
99    SLDataLocator_URI loc_uri;
100    loc_uri.locatorType = SL_DATALOCATOR_URI;
101    loc_uri.URI = (SLchar *) "wav/frog.wav";
102    SLDataFormat_MIME format_mime;
103    format_mime.formatType = SL_DATAFORMAT_MIME;
104    format_mime.mimeType = NULL;
105    format_mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
106    SLDataSource audioSrc;
107    audioSrc.pLocator = &loc_uri;
108    audioSrc.pFormat = &format_mime;
109    // configure audio sink
110    SLDataLocator_OutputMix loc_outmix;
111    loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
112    loc_outmix.outputMix = outputMixObject;
113    SLDataSink audioSnk;
114    audioSnk.pLocator = &loc_outmix;
115    audioSnk.pFormat = NULL;
116    // create audio player
117    SLInterfaceID ids[1] = {SL_IID_SEEK};
118    SLboolean req[1] = {SL_BOOLEAN_TRUE};
119    SLresult result = (*engineEngine)->CreateAudioPlayer(engineEngine, &p->mObject, &audioSrc,
120            &audioSnk, 1, ids, req);
121    if (SL_RESULT_SUCCESS != result)
122        return STATE_ERROR;
123    return STATE_CREATED;
124}
125
126State_t actionRealize(Player_pt p)
127{
128    assert(NULL != p->mObject);
129    // realize the player
130    SLresult result = (*p->mObject)->Realize(p->mObject, SL_BOOLEAN_FALSE);
131    assert(SL_RESULT_SUCCESS == result);
132    // get interfaces
133    result = (*p->mObject)->GetInterface(p->mObject, SL_IID_PLAY, &p->mPlay);
134    assert(SL_RESULT_SUCCESS == result);
135    result = (*p->mObject)->GetInterface(p->mObject, SL_IID_SEEK, &p->mSeek);
136    assert(SL_RESULT_SUCCESS == result);
137    return STATE_REALIZED;
138}
139
140State_t actionSleep(Player_pt p)
141{
142    unsigned us = 1000 + (rand() & 0xFFFFF);
143    usleep(us);
144    return STATE_UNCHANGED;
145}
146
147#if 0
148State_t actionSleep3(Player_pt p)
149{
150    sleep(3);
151    return STATE_IDLE;
152}
153#endif
154
155State_t actionTerminateIfDone(Player_pt p)
156{
157    if (countTransitions >= maxTransitions) {
158        assert(NULL == p->mObject);
159        // clean up output mix and engine
160        assert(NULL != outputMixObject);
161        (*outputMixObject)->Destroy(outputMixObject);
162        outputMixObject = NULL;
163        assert(NULL != engineObject);
164        (*engineObject)->Destroy(engineObject);
165        engineObject = NULL;
166        return STATE_TERMINAL;
167    } else
168        return STATE_UNCHANGED;
169}
170
171State_t actionInitialize(Player_pt p)
172{
173    // create engine
174    SLresult result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
175    assert(SL_RESULT_SUCCESS == result);
176    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
177    assert(SL_RESULT_SUCCESS == result);
178    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
179    assert(SL_RESULT_SUCCESS == result);
180
181    // create output mix
182    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
183    assert(SL_RESULT_SUCCESS == result);
184    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
185    assert(SL_RESULT_SUCCESS == result);
186
187    return STATE_NONEXISTENT;
188}
189
190typedef struct {
191    State_t mEntryState;
192    Action_pt mAction;
193    unsigned mProbability;
194    const char *mActionName;
195    unsigned mCount;
196} Transition_t;
197
198Transition_t transitionTable[] = {
199#define _(entryState, action, probability) {entryState, action, probability, #action, 0},
200    _(STATE_INITIAL, actionInitialize, 1)
201    _(STATE_CREATED, actionDestroy, 1)
202    _(STATE_CREATED, actionRealize, 1)
203    _(STATE_CREATED, actionSleep, 1)
204    _(STATE_NONEXISTENT, actionCreate, 1)
205    _(STATE_NONEXISTENT, actionSleep, 1)
206    _(STATE_PAUSED, actionDestroy, 1)
207    _(STATE_PAUSED, actionPause, 1)
208    _(STATE_PAUSED, actionPlay, 1)
209    _(STATE_PAUSED, actionRewind, 1)
210    _(STATE_PAUSED, actionSleep, 1)
211    _(STATE_PAUSED, actionStop, 1)
212    _(STATE_PLAYING, actionDestroy, 1)
213    _(STATE_PLAYING, actionPause, 1)
214    _(STATE_PLAYING, actionPlay, 1)
215    _(STATE_PLAYING, actionRewind, 1)
216    _(STATE_PLAYING, actionSleep, 1)
217    _(STATE_PLAYING, actionStop, 1)
218    _(STATE_REALIZED, actionDestroy, 1)
219    _(STATE_REALIZED, actionPause, 1)
220    _(STATE_REALIZED, actionPlay, 1)
221    _(STATE_REALIZED, actionSleep, 1)
222    _(STATE_REALIZED, actionStop, 1)
223    _(STATE_STOPPED, actionDestroy, 1)
224    _(STATE_STOPPED, actionPause, 1)
225    _(STATE_STOPPED, actionPlay, 1)
226    _(STATE_STOPPED, actionRewind, 1)
227    _(STATE_STOPPED, actionSleep, 1)
228    _(STATE_STOPPED, actionStop, 1)
229//    _(STATE_STOPPED, actionSleep3, 1)
230//    _(STATE_IDLE, actionDestroy, 1)
231    _(STATE_NONEXISTENT, actionTerminateIfDone, 1)
232};
233
234int main(int argc, char **argv)
235{
236    int i;
237    for (i = 1; i < argc; ++i) {
238        char *arg = argv[i];
239        if (arg[0] != '-')
240            break;
241        if (!strncmp(arg, "-m", 2)) {
242            maxTransitions = atoi(&arg[2]);
243        } else {
244            fprintf(stderr, "Unknown option %s\n", arg);
245        }
246    }
247    unsigned possibleTransitions = sizeof(transitionTable) / sizeof(transitionTable[0]);
248    Player_t player;
249    player.mObject = NULL;
250    player.mPlay = NULL;
251    player.mSeek = NULL;
252    State_t currentState = STATE_INITIAL;
253    while (STATE_TERMINAL != currentState) {
254        unsigned matchingTransitions = 0;
255        unsigned totalProbability = 0;
256        for (i = 0; i < (int) possibleTransitions; ++i) {
257            if (currentState != transitionTable[i].mEntryState)
258                continue;
259            ++matchingTransitions;
260            totalProbability += transitionTable[i].mProbability;
261        }
262        if (matchingTransitions == 0) {
263            fprintf(stderr, "No matching transitions in state %d\n", currentState);
264            assert(SL_BOOLEAN_FALSE);
265            break;
266        }
267        if (totalProbability == 0) {
268            fprintf(stderr, "Found at least one matching transition in state %d, "
269                    "but with probability 0\n", currentState);
270            assert(SL_BOOLEAN_FALSE);
271            break;
272        }
273        unsigned choice = (rand() & 0x7FFFFFFF) % totalProbability;
274        totalProbability = 0;
275        for (i = 0; i < (int) possibleTransitions; ++i) {
276            if (currentState != transitionTable[i].mEntryState)
277                continue;
278            totalProbability += transitionTable[i].mProbability;
279            if (totalProbability <= choice)
280                continue;
281            ++transitionTable[i].mCount;
282            ++countTransitions;
283            printf("[%d] Selecting transition %s in state %d for the %u time\n", countTransitions,
284                    transitionTable[i].mActionName, currentState, transitionTable[i].mCount);
285            State_t nextState = (*transitionTable[i].mAction)(&player);
286            if (STATE_UNCHANGED != nextState)
287                currentState = nextState;
288            goto found;
289        }
290        fprintf(stderr, "This shouldn't happen\n");
291        assert(SL_BOOLEAN_FALSE);
292found:
293        ;
294    }
295    for (i = 0; i < (int) possibleTransitions; ++i) {
296        printf("state %d action %s count %u\n",
297            transitionTable[i].mEntryState,
298            transitionTable[i].mActionName,
299            transitionTable[i].mCount);
300    }
301    return EXIT_SUCCESS;
302}
303