8 #include <SLES/OpenSLES.h>
9 #include <SLES/OpenSLES_Android.h>
15 void play(char const* uri
);
17 * This allows setting the stream type (default:SL_ANDROID_STREAM_MEDIA):
18 * SL_ANDROID_STREAM_ALARM - same as android.media.AudioManager.STREAM_ALARM
19 * SL_ANDROID_STREAM_MEDIA - same as android.media.AudioManager.STREAM_MUSIC
20 * SL_ANDROID_STREAM_NOTIFICATION - same as android.media.AudioManager.STREAM_NOTIFICATION
21 * SL_ANDROID_STREAM_RING - same as android.media.AudioManager.STREAM_RING
22 * SL_ANDROID_STREAM_SYSTEM - same as android.media.AudioManager.STREAM_SYSTEM
23 * SL_ANDROID_STREAM_VOICE - same as android.media.AudioManager.STREAM_VOICE_CALL
25 void setStreamType(SLint32 streamType
) { this->androidStreamType
= streamType
; }
27 SLObjectItf mSlEngineObject
{NULL
};
28 SLEngineItf mSlEngineInterface
{NULL
};
29 SLObjectItf mSlOutputMixObject
{NULL
};
30 SLint32 androidStreamType
{SL_ANDROID_STREAM_MEDIA
};
33 class MutexWithCondition
{
35 MutexWithCondition() {
36 pthread_mutex_init(&mutex
, NULL
);
37 pthread_cond_init(&condition
, NULL
);
38 pthread_mutex_lock(&mutex
);
40 ~MutexWithCondition() { pthread_mutex_unlock(&mutex
); }
41 void waitFor() { while (!occurred
) pthread_cond_wait(&condition
, &mutex
); }
42 /** From waking thread. */
43 void lockAndSignal() {
44 pthread_mutex_lock(&mutex
);
46 pthread_cond_signal(&condition
);
47 pthread_mutex_unlock(&mutex
);
50 volatile bool occurred
{false};
51 pthread_mutex_t mutex
;
52 pthread_cond_t condition
;
55 AudioPlayer
::AudioPlayer() {
56 // "OpenSL ES for Android is designed for multi-threaded applications, and is thread-safe.
57 // OpenSL ES for Android supports a single engine per application, and up to 32 objects.
58 // Available device memory and CPU may further restrict the usable number of objects.
59 // slCreateEngine recognizes, but ignores, these engine options: SL_ENGINEOPTION_THREADSAFE SL_ENGINEOPTION_LOSSOFCONTROL"
60 SLresult result
= slCreateEngine(&mSlEngineObject
,
61 /*numOptions=*/0, /*options=*/NULL
,
62 /*numWantedInterfaces=*/0, /*wantedInterfaces=*/NULL
, /*wantedInterfacesRequired=*/NULL
);
63 assert(SL_RESULT_SUCCESS
== result
);
65 result
= (*mSlEngineObject
)->Realize(mSlEngineObject
, SL_BOOLEAN_FALSE
);
66 assert(SL_RESULT_SUCCESS
== result
);
68 result
= (*mSlEngineObject
)->GetInterface(mSlEngineObject
, SL_IID_ENGINE
, &mSlEngineInterface
);
69 assert(SL_RESULT_SUCCESS
== result
);
71 SLuint32
const numWantedInterfaces
= 0;
72 result
= (*mSlEngineInterface
)->CreateOutputMix(mSlEngineInterface
, &mSlOutputMixObject
, numWantedInterfaces
, NULL
, NULL
);
73 assert(SL_RESULT_SUCCESS
== result
);
75 result
= (*mSlOutputMixObject
)->Realize(mSlOutputMixObject
, SL_BOOLEAN_FALSE
);
76 assert(SL_RESULT_SUCCESS
== result
);
80 void opensl_prefetch_callback(SLPrefetchStatusItf caller
, void* pContext
, SLuint32 event
) {
81 if (event
& SL_PREFETCHEVENT_STATUSCHANGE
) {
83 (*caller
)->GetFillLevel(caller
, &level
);
86 (*caller
)->GetPrefetchStatus(caller
, &status
);
87 if (status
== SL_PREFETCHSTATUS_UNDERFLOW
) {
88 // Level is 0 but we have SL_PREFETCHSTATUS_UNDERFLOW, implying an error.
89 printf("play-audio: underflow when prefetching data\n");
90 MutexWithCondition
* cond
= (MutexWithCondition
*) pContext
;
91 cond
->lockAndSignal();
97 void opensl_player_callback(SLPlayItf
/*caller*/, void* pContext
, SLuint32
/*event*/) {
98 MutexWithCondition
* condition
= (MutexWithCondition
*) pContext
;
99 condition
->lockAndSignal();
102 void AudioPlayer
::play(char const* uri
)
104 SLDataLocator_URI loc_uri
= {SL_DATALOCATOR_URI
, (SLchar
*) uri
};
105 SLDataFormat_MIME format_mime
= {SL_DATAFORMAT_MIME
, NULL
, SL_CONTAINERTYPE_UNSPECIFIED
};
106 SLDataSource audioSrc
= {&loc_uri
, &format_mime
};
108 SLDataLocator_OutputMix loc_outmix
= {SL_DATALOCATOR_OUTPUTMIX
, mSlOutputMixObject
};
109 SLDataSink audioSnk
= {&loc_outmix
, NULL
};
111 // SL_IID_ANDROIDCONFIGURATION is Android specific interface, SL_IID_PREFETCHSTATUS is general:
112 SLuint32
const numWantedInterfaces
= 2;
113 SLInterfaceID wantedInterfaces
[numWantedInterfaces
]{ SL_IID_ANDROIDCONFIGURATION
, SL_IID_PREFETCHSTATUS
};
114 SLboolean wantedInterfacesRequired
[numWantedInterfaces
]{ SL_BOOLEAN_TRUE
, SL_BOOLEAN_TRUE
};
116 SLObjectItf uriPlayerObject
= NULL
;
117 SLresult result
= (*mSlEngineInterface
)->CreateAudioPlayer(mSlEngineInterface
, &uriPlayerObject
, &audioSrc
, &audioSnk
,
118 numWantedInterfaces
, wantedInterfaces
, wantedInterfacesRequired
);
119 assert(SL_RESULT_SUCCESS
== result
);
121 // Android specific interface - usage:
122 // SLresult (*GetInterface) (SLObjectItf self, const SLInterfaceID iid, void* pInterface);
123 // This function gives different interfaces. One is android-specific, from
124 // <SLES/OpenSLES_AndroidConfiguration.h>, done before realization:
125 SLAndroidConfigurationItf androidConfig
;
126 result
= (*uriPlayerObject
)->GetInterface(uriPlayerObject
, SL_IID_ANDROIDCONFIGURATION
, &androidConfig
);
127 assert(SL_RESULT_SUCCESS
== result
);
129 result
= (*androidConfig
)->SetConfiguration(androidConfig
, SL_ANDROID_KEY_STREAM_TYPE
, &this->androidStreamType
, sizeof(SLint32
));
130 assert(SL_RESULT_SUCCESS
== result
);
132 // We now Realize(). Note that the android config needs to be done before, but getting the SLPrefetchStatusItf after.
133 result
= (*uriPlayerObject
)->Realize(uriPlayerObject
, /*async=*/SL_BOOLEAN_FALSE
);
134 assert(SL_RESULT_SUCCESS
== result
);
136 SLPrefetchStatusItf prefetchInterface
;
137 result
= (*uriPlayerObject
)->GetInterface(uriPlayerObject
, SL_IID_PREFETCHSTATUS
, &prefetchInterface
);
138 assert(SL_RESULT_SUCCESS
== result
);
140 SLPlayItf uriPlayerPlay
= NULL
;
141 result
= (*uriPlayerObject
)->GetInterface(uriPlayerObject
, SL_IID_PLAY
, &uriPlayerPlay
);
142 assert(SL_RESULT_SUCCESS
== result
);
144 if (NULL
== uriPlayerPlay
) {
145 fprintf(stderr
, "Cannot play '%s'\n", uri
);
147 result
= (*uriPlayerPlay
)->SetCallbackEventsMask(uriPlayerPlay
, SL_PLAYEVENT_HEADSTALLED
| SL_PLAYEVENT_HEADATEND
);
148 assert(SL_RESULT_SUCCESS
== result
);
150 MutexWithCondition condition
;
151 result
= (*uriPlayerPlay
)->RegisterCallback(uriPlayerPlay
, opensl_player_callback
, &condition
);
152 assert(SL_RESULT_SUCCESS
== result
);
154 result
= (*prefetchInterface
)->RegisterCallback(prefetchInterface
, opensl_prefetch_callback
, &condition
);
155 assert(SL_RESULT_SUCCESS
== result
);
156 result
= (*prefetchInterface
)->SetCallbackEventsMask(prefetchInterface
, SL_PREFETCHEVENT_FILLLEVELCHANGE
| SL_PREFETCHEVENT_STATUSCHANGE
);
158 // "For an audio player with URI data source, Object::Realize allocates resources but does not
159 // connect to the data source (i.e. "prepare") or begin pre-fetching data. These occur once the
160 // player state is set to either SL_PLAYSTATE_PAUSED or SL_PLAYSTATE_PLAYING."
161 // - http://mobilepearls.com/labs/native-android-api/ndk/docs/opensles/index.html
162 result
= (*uriPlayerPlay
)->SetPlayState(uriPlayerPlay
, SL_PLAYSTATE_PLAYING
);
163 assert(SL_RESULT_SUCCESS
== result
);
168 if (uriPlayerObject
!= NULL
) (*uriPlayerObject
)->Destroy(uriPlayerObject
);
172 AudioPlayer
::~AudioPlayer()
174 // "Be sure to destroy all objects on exit from your application. Objects should be destroyed in reverse order of their creation,
175 // as it is not safe to destroy an object that has any dependent objects. For example, destroy in this order: audio players
176 // and recorders, output mix, then finally the engine."
177 if (mSlOutputMixObject
!= NULL
) { (*mSlOutputMixObject
)->Destroy(mSlOutputMixObject
); mSlOutputMixObject
= NULL
; }
178 if (mSlEngineObject
!= NULL
) { (*mSlEngineObject
)->Destroy(mSlEngineObject
); mSlEngineObject
= NULL
; }
182 int main(int argc
, char** argv
)
186 char* streamType
= NULL
;
187 while ((c
= getopt(argc
, argv
, "hs:")) != -1) {
190 case '?': help
= true; break;
191 case 's': streamType
= optarg
; break;
195 if (help
|| optind
== argc
) {
196 printf("usage: play-audio [-s streamtype] [files]\n");
202 if (streamType
!= NULL
) {
203 SLint32 streamTypeEnum
;
204 if (strcmp("alarm", streamType
) == 0) {
205 streamTypeEnum
= SL_ANDROID_STREAM_ALARM
;
206 } else if (strcmp("media", streamType
) == 0) {
207 streamTypeEnum
= SL_ANDROID_STREAM_MEDIA
;
208 } else if (strcmp("notification", streamType
) == 0) {
209 streamTypeEnum
= SL_ANDROID_STREAM_NOTIFICATION
;
210 } else if (strcmp("ring", streamType
) == 0) {
211 streamTypeEnum
= SL_ANDROID_STREAM_RING
;
212 } else if (strcmp("system", streamType
) == 0) {
213 streamTypeEnum
= SL_ANDROID_STREAM_SYSTEM
;
214 } else if (strcmp("voice", streamType
) == 0) {
215 streamTypeEnum
= SL_ANDROID_STREAM_VOICE
;
217 fprintf(stderr
, "play-audio: invalid streamtype '%s'\n", streamType
);
220 player
.setStreamType(streamTypeEnum
);
223 for (int i
= optind
; i
< argc
; i
++) {
224 if (access(argv
[i
], R_OK
) != 0) {
225 fprintf(stderr
, "play-audio: '%s' is not a readable file\n", argv
[i
]);
230 for (int i
= optind
; i
< argc
; i
++) {
231 player
.play(argv
[i
]);