chiark / gitweb /
vim: Update patch number
[termux-packages] / packages / play-audio / play-audio.cpp
1 #include <assert.h>
2 #include <getopt.h>
3 #include <pthread.h>
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <SLES/OpenSLES.h>
9 #include <SLES/OpenSLES_Android.h>
10
11 class AudioPlayer {
12         public:
13                 AudioPlayer();
14                 ~AudioPlayer();
15                 void play(char const* uri);
16         private:
17                 SLObjectItf mSlEngineObject{NULL};
18                 SLEngineItf mSlEngineInterface{NULL};
19                 SLObjectItf mSlOutputMixObject{NULL};
20 };
21
22 class MutexWithCondition {
23         public:
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);
30                         occurred = true;
31                         pthread_cond_signal(&condition);
32                         pthread_mutex_unlock(&mutex);
33                 }
34         private:
35                 volatile bool occurred{false};
36                 pthread_mutex_t mutex{PTHREAD_MUTEX_INITIALIZER};
37                 pthread_cond_t condition{PTHREAD_COND_INITIALIZER};
38 };
39
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);
49
50         result = (*mSlEngineObject)->Realize(mSlEngineObject, SL_BOOLEAN_FALSE);
51         assert(SL_RESULT_SUCCESS == result);
52
53         result = (*mSlEngineObject)->GetInterface(mSlEngineObject, SL_IID_ENGINE, &mSlEngineInterface);
54         assert(SL_RESULT_SUCCESS == result);
55
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);
61
62         result = (*mSlOutputMixObject)->Realize(mSlOutputMixObject, SL_BOOLEAN_FALSE);
63         assert(SL_RESULT_SUCCESS == result);
64
65 }
66
67 void opensl_prefetch_callback(SLPrefetchStatusItf caller, void* pContext, SLuint32 event) {
68         if (event & SL_PREFETCHEVENT_STATUSCHANGE) {
69                 SLpermille level = 0;
70                 (*caller)->GetFillLevel(caller, &level);
71                 if (level == 0) {
72                         SLuint32 status;
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();
79                         }
80                 }
81         }
82 }
83
84 void opensl_player_callback(SLPlayItf /*caller*/, void* pContext, SLuint32 /*event*/) {
85         MutexWithCondition* condition = (MutexWithCondition*) pContext;
86         condition->lockAndSignal();
87 }
88
89 void AudioPlayer::play(char const* uri)
90 {
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};
94
95         SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, mSlOutputMixObject};
96         SLDataSink audioSnk = {&loc_outmix, NULL};
97
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,
102                 SL_IID_VOLUME,
103                 SL_IID_PREFETCHSTATUS,
104                 SL_IID_PLAYBACKRATE,
105                 SL_IID_EFFECTSEND
106         };
107         SLboolean wantedInterfacesRequired[numWantedInterfaces]{ SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
108
109         SLObjectItf uriPlayerObject = NULL;
110         SLresult result = (*mSlEngineInterface)->CreateAudioPlayer(mSlEngineInterface, &uriPlayerObject, &audioSrc, &audioSnk,
111                         numWantedInterfaces, wantedInterfaces, wantedInterfacesRequired);
112         assert(SL_RESULT_SUCCESS == result);
113
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);
121
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);
138
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);
142
143         SLPrefetchStatusItf prefetchInterface;
144         result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_PREFETCHSTATUS, &prefetchInterface);
145         assert(SL_RESULT_SUCCESS == result);
146
147         SLPlayItf uriPlayerPlay = NULL;
148         result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_PLAY, &uriPlayerPlay);
149         assert(SL_RESULT_SUCCESS == result);
150
151         SLPlaybackRateItf playbackRateInterface;
152         result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_PLAYBACKRATE, &playbackRateInterface);
153         assert(SL_RESULT_SUCCESS == result);
154
155         if (NULL == uriPlayerPlay) {
156                 fprintf(stderr, "Cannot play '%s'\n", uri);
157         } else {
158                 result = (*uriPlayerPlay)->SetCallbackEventsMask(uriPlayerPlay, SL_PLAYEVENT_HEADSTALLED | SL_PLAYEVENT_HEADATEND);
159                 assert(SL_RESULT_SUCCESS == result);
160
161                 MutexWithCondition condition;
162                 result = (*uriPlayerPlay)->RegisterCallback(uriPlayerPlay, opensl_player_callback, &condition);
163                 assert(SL_RESULT_SUCCESS == result);
164
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);
168
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);
175
176                 condition.waitFor();
177         }
178
179         if (uriPlayerObject != NULL) (*uriPlayerObject)->Destroy(uriPlayerObject);
180 }
181
182
183 AudioPlayer::~AudioPlayer()
184 {
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; }
190 }
191
192
193 int main(int argc, char** argv)
194 {
195         bool help = false;
196         int c;
197         while ((c = getopt(argc, argv, "h")) != -1) {
198                 switch (c) {
199                         case 'h': help = true; break;
200                 }
201         }
202
203         if (help || optind == argc) {
204                 printf("usage: %s [files]\n", argv[0]);
205                 exit(0);
206         }
207
208         AudioPlayer player;
209         for (int i = optind; i < argc; i++) player.play(argv[i]);
210
211         return 0;
212 }