8 #include <SLES/OpenSLES.h>
9 #include <SLES/OpenSLES_Android.h>
15 void play(char const* uri
);
17 SLObjectItf mSlEngineObject
{NULL
};
18 SLEngineItf mSlEngineInterface
{NULL
};
19 SLObjectItf mSlOutputMixObject
{NULL
};
22 class MutexWithCondition
{
24 MutexWithCondition() { pthread_mutex_lock(&mutex
); }
25 ~MutexWithCondition() { pthread_mutex_unlock(&mutex
); }
26 void waitFor() { while (!occurred
) pthread_cond_wait(&condition
, &mutex
); }
27 /** From waking thread. */
28 void lockAndSignal() {
29 pthread_mutex_lock(&mutex
);
31 pthread_cond_signal(&condition
);
32 pthread_mutex_unlock(&mutex
);
35 volatile bool occurred
{false};
36 pthread_mutex_t mutex
{PTHREAD_MUTEX_INITIALIZER
};
37 pthread_cond_t condition
{PTHREAD_COND_INITIALIZER
};
40 AudioPlayer
::AudioPlayer() {
41 // "OpenSL ES for Android is designed for multi-threaded applications, and is thread-safe.
42 // OpenSL ES for Android supports a single engine per application, and up to 32 objects.
43 // Available device memory and CPU may further restrict the usable number of objects.
44 // slCreateEngine recognizes, but ignores, these engine options: SL_ENGINEOPTION_THREADSAFE SL_ENGINEOPTION_LOSSOFCONTROL"
45 SLresult result
= slCreateEngine(&mSlEngineObject
,
46 /*numOptions=*/0, /*options=*/NULL
,
47 /*numWantedInterfaces=*/0, /*wantedInterfaces=*/NULL
, /*wantedInterfacesRequired=*/NULL
);
48 assert(SL_RESULT_SUCCESS
== result
);
50 result
= (*mSlEngineObject
)->Realize(mSlEngineObject
, SL_BOOLEAN_FALSE
);
51 assert(SL_RESULT_SUCCESS
== result
);
53 result
= (*mSlEngineObject
)->GetInterface(mSlEngineObject
, SL_IID_ENGINE
, &mSlEngineInterface
);
54 assert(SL_RESULT_SUCCESS
== result
);
56 SLuint32
const numWantedInterfaces
= 1;
57 SLInterfaceID wantedInterfaces
[numWantedInterfaces
]{ SL_IID_ENVIRONMENTALREVERB
};
58 SLboolean wantedInterfacesRequired
[numWantedInterfaces
]{ SL_BOOLEAN_TRUE
};
59 result
= (*mSlEngineInterface
)->CreateOutputMix(mSlEngineInterface
, &mSlOutputMixObject
, numWantedInterfaces
, wantedInterfaces
, wantedInterfacesRequired
);
60 assert(SL_RESULT_SUCCESS
== result
);
62 result
= (*mSlOutputMixObject
)->Realize(mSlOutputMixObject
, SL_BOOLEAN_FALSE
);
63 assert(SL_RESULT_SUCCESS
== result
);
67 void opensl_prefetch_callback(SLPrefetchStatusItf caller
, void* pContext
, SLuint32 event
) {
68 if (event
& SL_PREFETCHEVENT_STATUSCHANGE
) {
70 (*caller
)->GetFillLevel(caller
, &level
);
73 (*caller
)->GetPrefetchStatus(caller
, &status
);
74 if (status
== SL_PREFETCHSTATUS_UNDERFLOW
) {
75 // Level is 0 but we have SL_PREFETCHSTATUS_UNDERFLOW, implying an error.
76 printf("- ERROR: Underflow when prefetching data and fill level zero\n");
77 MutexWithCondition
* cond
= (MutexWithCondition
*) pContext
;
78 cond
->lockAndSignal();
84 void opensl_player_callback(SLPlayItf
/*caller*/, void* pContext
, SLuint32
/*event*/) {
85 MutexWithCondition
* condition
= (MutexWithCondition
*) pContext
;
86 condition
->lockAndSignal();
89 void AudioPlayer
::play(char const* uri
)
91 SLDataLocator_URI loc_uri
= {SL_DATALOCATOR_URI
, (SLchar
*) uri
};
92 SLDataFormat_MIME format_mime
= {SL_DATAFORMAT_MIME
, NULL
, SL_CONTAINERTYPE_UNSPECIFIED
};
93 SLDataSource audioSrc
= {&loc_uri
, &format_mime
};
95 SLDataLocator_OutputMix loc_outmix
= {SL_DATALOCATOR_OUTPUTMIX
, mSlOutputMixObject
};
96 SLDataSink audioSnk
= {&loc_outmix
, NULL
};
98 // SL_IID_ANDROIDCONFIGURATION is Android specific interface, SL_IID_VOLUME is general:
99 SLuint32
const numWantedInterfaces
= 5;
100 SLInterfaceID wantedInterfaces
[numWantedInterfaces
]{
101 SL_IID_ANDROIDCONFIGURATION
,
103 SL_IID_PREFETCHSTATUS
,
107 SLboolean wantedInterfacesRequired
[numWantedInterfaces
]{ SL_BOOLEAN_TRUE
, SL_BOOLEAN_TRUE
, SL_BOOLEAN_TRUE
, SL_BOOLEAN_TRUE
, SL_BOOLEAN_TRUE
};
109 SLObjectItf uriPlayerObject
= NULL
;
110 SLresult result
= (*mSlEngineInterface
)->CreateAudioPlayer(mSlEngineInterface
, &uriPlayerObject
, &audioSrc
, &audioSnk
,
111 numWantedInterfaces
, wantedInterfaces
, wantedInterfacesRequired
);
112 assert(SL_RESULT_SUCCESS
== result
);
114 // Android specific interface - usage:
115 // SLresult (*GetInterface) (SLObjectItf self, const SLInterfaceID iid, void* pInterface);
116 // This function gives different interfaces. One is android-specific, from
117 // <SLES/OpenSLES_AndroidConfiguration.h>, done before realization:
118 SLAndroidConfigurationItf androidConfig
;
119 result
= (*uriPlayerObject
)->GetInterface(uriPlayerObject
, SL_IID_ANDROIDCONFIGURATION
, &androidConfig
);
120 assert(SL_RESULT_SUCCESS
== result
);
122 // This allows setting the stream type (default:SL_ANDROID_STREAM_MEDIA):
123 /* same as android.media.AudioManager.STREAM_VOICE_CALL */
124 // #define SL_ANDROID_STREAM_VOICE ((SLint32) 0x00000000)
125 /* same as android.media.AudioManager.STREAM_SYSTEM */
126 // #define SL_ANDROID_STREAM_SYSTEM ((SLint32) 0x00000001)
127 /* same as android.media.AudioManager.STREAM_RING */
128 // #define SL_ANDROID_STREAM_RING ((SLint32) 0x00000002)
129 /* same as android.media.AudioManager.STREAM_MUSIC */
130 // #define SL_ANDROID_STREAM_MEDIA ((SLint32) 0x00000003)
131 /* same as android.media.AudioManager.STREAM_ALARM */
132 // #define SL_ANDROID_STREAM_ALARM ((SLint32) 0x00000004)
133 /* same as android.media.AudioManager.STREAM_NOTIFICATION */
134 // #define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005)
135 SLint32 androidStreamType
= SL_ANDROID_STREAM_ALARM
;
136 result
= (*androidConfig
)->SetConfiguration(androidConfig
, SL_ANDROID_KEY_STREAM_TYPE
, &androidStreamType
, sizeof(SLint32
));
137 assert(SL_RESULT_SUCCESS
== result
);
139 // We now Realize(). Note that the android config needs to be done before, but getting the SLPrefetchStatusItf after.
140 result
= (*uriPlayerObject
)->Realize(uriPlayerObject
, /*async=*/SL_BOOLEAN_FALSE
);
141 assert(SL_RESULT_SUCCESS
== result
);
143 SLPrefetchStatusItf prefetchInterface
;
144 result
= (*uriPlayerObject
)->GetInterface(uriPlayerObject
, SL_IID_PREFETCHSTATUS
, &prefetchInterface
);
145 assert(SL_RESULT_SUCCESS
== result
);
147 SLPlayItf uriPlayerPlay
= NULL
;
148 result
= (*uriPlayerObject
)->GetInterface(uriPlayerObject
, SL_IID_PLAY
, &uriPlayerPlay
);
149 assert(SL_RESULT_SUCCESS
== result
);
151 SLPlaybackRateItf playbackRateInterface
;
152 result
= (*uriPlayerObject
)->GetInterface(uriPlayerObject
, SL_IID_PLAYBACKRATE
, &playbackRateInterface
);
153 assert(SL_RESULT_SUCCESS
== result
);
155 if (NULL
== uriPlayerPlay
) {
156 fprintf(stderr
, "Cannot play '%s'\n", uri
);
158 result
= (*uriPlayerPlay
)->SetCallbackEventsMask(uriPlayerPlay
, SL_PLAYEVENT_HEADSTALLED
| SL_PLAYEVENT_HEADATEND
);
159 assert(SL_RESULT_SUCCESS
== result
);
161 MutexWithCondition condition
;
162 result
= (*uriPlayerPlay
)->RegisterCallback(uriPlayerPlay
, opensl_player_callback
, &condition
);
163 assert(SL_RESULT_SUCCESS
== result
);
165 result
= (*prefetchInterface
)->RegisterCallback(prefetchInterface
, opensl_prefetch_callback
, &condition
);
166 assert(SL_RESULT_SUCCESS
== result
);
167 result
= (*prefetchInterface
)->SetCallbackEventsMask(prefetchInterface
, SL_PREFETCHEVENT_FILLLEVELCHANGE
| SL_PREFETCHEVENT_STATUSCHANGE
);
169 // "For an audio player with URI data source, Object::Realize allocates resources but does not
170 // connect to the data source (i.e. "prepare") or begin pre-fetching data. These occur once the
171 // player state is set to either SL_PLAYSTATE_PAUSED or SL_PLAYSTATE_PLAYING."
172 // - http://mobilepearls.com/labs/native-android-api/ndk/docs/opensles/index.html
173 result
= (*uriPlayerPlay
)->SetPlayState(uriPlayerPlay
, SL_PLAYSTATE_PLAYING
);
174 assert(SL_RESULT_SUCCESS
== result
);
179 if (uriPlayerObject
!= NULL
) (*uriPlayerObject
)->Destroy(uriPlayerObject
);
183 AudioPlayer
::~AudioPlayer()
185 // "Be sure to destroy all objects on exit from your application. Objects should be destroyed in reverse order of their creation,
186 // as it is not safe to destroy an object that has any dependent objects. For example, destroy in this order: audio players
187 // and recorders, output mix, then finally the engine."
188 if (mSlOutputMixObject
!= NULL
) { (*mSlOutputMixObject
)->Destroy(mSlOutputMixObject
); mSlOutputMixObject
= NULL
; }
189 if (mSlEngineObject
!= NULL
) { (*mSlEngineObject
)->Destroy(mSlEngineObject
); mSlEngineObject
= NULL
; }
193 int main(int argc
, char** argv
)
197 while ((c
= getopt(argc
, argv
, "h")) != -1) {
199 case 'h': help
= true; break;
203 if (help
|| optind
== argc
) {
204 printf("usage: %s [files]\n", argv
[0]);
209 for (int i
= optind
; i
< argc
; i
++) player
.play(argv
[i
]);