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 #include <assert.h> 18 #include <stdlib.h> 19 #include <stdio.h> 20 #include <string.h> 21 #include <unistd.h> 22 #include <sys/time.h> 23 #include <fcntl.h> 24 25 #include <SLES/OpenSLES.h> 26 #ifdef ANDROID 27 #include <SLES/OpenSLES_Android.h> 28 #endif 29 30 31 #define MAX_NUMBER_INTERFACES 4 32 33 #define TIME_S_BETWEEN_SETTING_CHANGE 3 34 35 //----------------------------------------------------------------- 36 /* Exits the application if an error is encountered */ 37 #define ExitOnError(x) ExitOnErrorFunc(x,__LINE__) 38 39 void ExitOnErrorFunc( SLresult result , int line) 40 { 41 if (SL_RESULT_SUCCESS != result) { 42 fprintf(stderr, "%u error code encountered at line %d, exiting\n", result, line); 43 exit(EXIT_FAILURE); 44 } 45 } 46 47 // Prefetch status callback 48 49 #define PREFETCHEVENT_ERROR_CANDIDATE \ 50 (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE) 51 52 SLboolean errorInPrefetchCallback = SL_BOOLEAN_FALSE; 53 54 void prefetch_callback(SLPrefetchStatusItf caller, void *context, SLuint32 event) 55 { 56 SLresult result; 57 assert(context == NULL); 58 SLpermille level; 59 result = (*caller)->GetFillLevel(caller, &level); 60 ExitOnError(result); 61 SLuint32 status; 62 result = (*caller)->GetPrefetchStatus(caller, &status); 63 ExitOnError(result); 64 if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE)) 65 && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) { 66 errorInPrefetchCallback = SL_BOOLEAN_TRUE; 67 } 68 } 69 70 //----------------------------------------------------------------- 71 72 /* Play an audio path and feed a global reverb */ 73 void TestSendToPresetReverb( SLObjectItf sl, const char* path, int preset, SLmillibel directLevel, 74 SLmillibel sendLevel, bool alwaysOn, bool useFd, bool loop) 75 { 76 SLresult result; 77 SLEngineItf EngineItf; 78 79 /* Objects this application uses: one player and an ouput mix */ 80 SLObjectItf player, outputMix; 81 82 /* Source of audio data to play */ 83 SLDataSource audioSource; 84 #ifdef ANDROID 85 SLDataLocator_AndroidFD locatorFd; 86 #endif 87 SLDataLocator_URI locatorUri; 88 SLDataFormat_MIME mime; 89 90 /* Data sinks for the audio player */ 91 SLDataSink audioSink; 92 SLDataLocator_OutputMix locator_outputmix; 93 94 /* Interfaces for the audio player */ 95 SLPlayItf playItf; 96 SLPrefetchStatusItf prefetchItf; 97 SLEffectSendItf effectSendItf; 98 SLSeekItf seekItf; 99 100 /* Interface for the output mix */ 101 SLPresetReverbItf reverbItf; 102 103 SLboolean required[MAX_NUMBER_INTERFACES]; 104 SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; 105 106 /* Get the SL Engine Interface which is implicit */ 107 result = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf); 108 ExitOnError(result); 109 110 /* Initialize arrays required[] and iidArray[] */ 111 for (int i=0 ; i < MAX_NUMBER_INTERFACES ; i++) { 112 required[i] = SL_BOOLEAN_FALSE; 113 iidArray[i] = SL_IID_NULL; 114 } 115 116 /* ------------------------------------------------------ */ 117 /* Configuration of the output mix */ 118 119 /* Set arrays required[] and iidArray[] for required interfaces */ 120 required[0] = SL_BOOLEAN_TRUE; 121 iidArray[0] = SL_IID_PRESETREVERB; 122 123 /* Create Output Mix object to be used by the player */ 124 result = (*EngineItf)->CreateOutputMix(EngineItf, &outputMix, 1, iidArray, required); 125 ExitOnError(result); 126 127 /* Realize the Output Mix object in synchronous mode */ 128 result = (*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE); 129 ExitOnError(result); 130 131 /* Get the SLPresetReverbItf for the output mix */ 132 result = (*outputMix)->GetInterface(outputMix, SL_IID_PRESETREVERB, (void*)&reverbItf); 133 ExitOnError(result); 134 135 /* Setup the data sink structure */ 136 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 137 locator_outputmix.outputMix = outputMix; 138 audioSink.pLocator = (void*)&locator_outputmix; 139 audioSink.pFormat = NULL; 140 141 /* Select the reverb preset */ 142 fprintf(stdout, "\nUsing preset "); 143 switch(preset) { 144 case SL_REVERBPRESET_NONE: 145 fprintf(stdout, "SL_REVERBPRESET_NONE, don't expect to hear reverb\n"); 146 break; 147 case SL_REVERBPRESET_SMALLROOM: fprintf(stdout, "SL_REVERBPRESET_SMALLROOM\n"); break; 148 case SL_REVERBPRESET_MEDIUMROOM: fprintf(stdout, "SL_REVERBPRESET_MEDIUMROOM\n"); break; 149 case SL_REVERBPRESET_LARGEROOM: fprintf(stdout, "SL_REVERBPRESET_LARGEROOM\n"); break; 150 case SL_REVERBPRESET_MEDIUMHALL: fprintf(stdout, "SL_REVERBPRESET_MEDIUMHALL\n"); break; 151 case SL_REVERBPRESET_LARGEHALL: fprintf(stdout, "SL_REVERBPRESET_LARGEHALL\n"); break; 152 case SL_REVERBPRESET_PLATE: fprintf(stdout, "SL_REVERBPRESET_PLATE\n"); break; 153 default: 154 fprintf(stdout, "unknown, use at your own risk\n"); break; 155 } 156 result = (*reverbItf)->SetPreset(reverbItf, preset); 157 ExitOnError(result); 158 159 /* ------------------------------------------------------ */ 160 /* Configuration of the player */ 161 162 /* Set arrays required[] and iidArray[] for required interfaces */ 163 /* (SLPlayItf is implicit) */ 164 required[0] = SL_BOOLEAN_TRUE; 165 iidArray[0] = SL_IID_PREFETCHSTATUS; 166 required[1] = SL_BOOLEAN_TRUE; 167 iidArray[1] = SL_IID_EFFECTSEND; 168 required[2] = SL_BOOLEAN_TRUE; 169 iidArray[2] = SL_IID_SEEK; 170 171 locatorUri.locatorType = SL_DATALOCATOR_URI; 172 locatorUri.URI = (SLchar *) path; 173 audioSource.pLocator = (void*)&locatorUri; 174 if (useFd) { 175 #ifdef ANDROID 176 /* Setup the data source structure for the URI */ 177 locatorFd.locatorType = SL_DATALOCATOR_ANDROIDFD; 178 int fd = open(path, O_RDONLY); 179 if (fd == -1) { 180 perror(path); 181 exit(EXIT_FAILURE); 182 } 183 locatorFd.fd = (SLint32) fd; 184 locatorFd.length = SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE; 185 locatorFd.offset = 0; 186 audioSource.pLocator = (void*)&locatorFd; 187 #else 188 fprintf(stderr, "option --fd is not supported\n"); 189 #endif 190 } 191 192 mime.formatType = SL_DATAFORMAT_MIME; 193 /* this is how ignored mime information is specified, according to OpenSL ES spec 194 * in 9.1.6 SLDataFormat_MIME and 8.23 SLMetadataTraversalItf GetChildInfo */ 195 mime.mimeType = (SLchar*)NULL; 196 mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED; 197 198 audioSource.pFormat = (void*)&mime; 199 200 /* Create the audio player */ 201 result = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, 3, 202 iidArray, required); 203 ExitOnError(result); 204 205 /* Realize the player in synchronous mode. */ 206 result = (*player)->Realize(player, SL_BOOLEAN_FALSE); ExitOnError(result); 207 fprintf(stdout, "URI example: after Realize\n"); 208 209 /* Get the SLPlayItf, SLPrefetchStatusItf and SLEffectSendItf interfaces for the player*/ 210 result = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); 211 ExitOnError(result); 212 213 result = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf); 214 ExitOnError(result); 215 result = (*prefetchItf)->RegisterCallback(prefetchItf, prefetch_callback, NULL); 216 ExitOnError(result); 217 result = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, 218 SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE); 219 ExitOnError(result); 220 221 result = (*player)->GetInterface(player, SL_IID_EFFECTSEND, (void*)&effectSendItf); 222 ExitOnError(result); 223 224 result = (*player)->GetInterface(player, SL_IID_SEEK, (void*)&seekItf); 225 ExitOnError(result); 226 227 fprintf(stdout, "Player configured\n"); 228 229 /* ------------------------------------------------------ */ 230 /* Playback and test */ 231 232 /* Start the data prefetching by setting the player to the paused state */ 233 result = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED ); 234 ExitOnError(result); 235 236 /* Wait until there's data to play */ 237 SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW; 238 while (prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) { 239 if (errorInPrefetchCallback) { 240 fprintf(stderr, "Error during prefetch, exiting\n"); 241 exit(EXIT_FAILURE); 242 } 243 usleep(100 * 1000); 244 (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus); 245 ExitOnError(result); 246 } 247 248 /* Get duration */ 249 SLmillisecond durationInMsec = SL_TIME_UNKNOWN; 250 result = (*playItf)->GetDuration(playItf, &durationInMsec); 251 ExitOnError(result); 252 if (durationInMsec == SL_TIME_UNKNOWN) { 253 printf("Duration unknown, assuming 10 seconds\n"); 254 durationInMsec = 10000; 255 } else { 256 printf("Duration is %.1f seconds\n", durationInMsec / 1000.0); 257 } 258 259 /* Feed the output mix' reverb from the audio player using the given send level */ 260 result = (*effectSendItf)->EnableEffectSend(effectSendItf, reverbItf, SL_BOOLEAN_TRUE, 261 sendLevel); 262 ExitOnError(result); 263 264 result = (*effectSendItf)->SetDirectLevel(effectSendItf, directLevel); 265 ExitOnError(result); 266 fprintf(stdout, "Set direct level to %dmB\n", directLevel); 267 268 result = (*effectSendItf)->SetSendLevel(effectSendItf, reverbItf, sendLevel); 269 ExitOnError(result); 270 fprintf(stdout, "Set send level to %dmB\n", sendLevel); 271 272 /* Enable looping */ 273 if (loop) { 274 result = (*seekItf)->SetLoop(seekItf, SL_BOOLEAN_TRUE, (SLmillisecond) 0, SL_TIME_UNKNOWN); 275 ExitOnError(result); 276 } 277 278 /* Start playback */ 279 result = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); 280 ExitOnError(result); 281 282 /* Disable preset reverb every TIME_S_BETWEEN_SETTING_CHANGE seconds unless always on */ 283 SLboolean previousEnabled = SL_BOOLEAN_FALSE; 284 SLuint32 playState; 285 for (;;) { 286 result = (*playItf)->GetPlayState(playItf, &playState); 287 ExitOnError(result); 288 if (playState != SL_PLAYSTATE_PLAYING) 289 break; 290 SLboolean enabled; 291 enabled = alwaysOn || !previousEnabled; 292 if (enabled != previousEnabled) { 293 result = (*reverbItf)->SetPreset(reverbItf, enabled ? preset : SL_REVERBPRESET_NONE); 294 fprintf(stdout, "SetPreset(%d)=%d\n", enabled ? preset : SL_REVERBPRESET_NONE, result); 295 ExitOnError(result); 296 previousEnabled = enabled; 297 if (enabled) { 298 fprintf(stdout, "Reverb on\n"); 299 } else { 300 fprintf(stdout, "Reverb off\n"); 301 } 302 } 303 usleep(TIME_S_BETWEEN_SETTING_CHANGE * 1000 * 1000); 304 } 305 306 /* Make sure player is stopped */ 307 assert(playState == SL_PLAYSTATE_STOPPED); 308 #if 0 309 fprintf(stdout, "Stopping playback\n"); 310 result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); 311 ExitOnError(result); 312 #endif 313 314 /* Destroy the player */ 315 (*player)->Destroy(player); 316 317 /* Destroy Output Mix object */ 318 (*outputMix)->Destroy(outputMix); 319 320 #ifdef ANDROID 321 if (useFd) 322 close(locatorFd.fd); 323 #endif 324 } 325 326 //----------------------------------------------------------------- 327 int main(int argc, char* const argv[]) 328 { 329 const char *programName = argv[0]; 330 SLresult result; 331 SLObjectItf sl; 332 333 fprintf(stdout, "OpenSL ES test %s: exercises SLEffectSendItf ", programName); 334 fprintf(stdout, "on AudioPlayer and SLPresetReverbItf on OutputMix.\n"); 335 fprintf(stdout, "Plays the sound file designated by the given path, "); 336 fprintf(stdout, "and sends a specified amount of energy to a global reverb\n"); 337 fprintf(stdout, "(sendLevel in mB), with a given direct level (in mB).\n"); 338 fprintf(stdout, "Every %d seconds, the reverb is turned on and off,\n", 339 TIME_S_BETWEEN_SETTING_CHANGE); 340 fprintf(stdout, "unless the --always-on option is specified before the path.\n"); 341 342 bool alwaysOn = false; 343 bool useFd = false; 344 bool loop = false; 345 int i; 346 for (i = 1; i < argc; ++i) { 347 const char *arg = argv[i]; 348 if (arg[0] != '-') 349 break; 350 if (!strcmp(arg, "--always-on")) { 351 alwaysOn = true; 352 } else if (!strcmp(arg, "--fd")) { 353 useFd = true; 354 } else if (!strcmp(arg, "--loop")) { 355 loop = true; 356 } else { 357 fprintf(stderr, "unknown option %s ignored\n", arg); 358 } 359 } 360 361 if (argc - i != 4) { 362 fprintf(stdout, "Usage: \t%s [--always-on] [--fd] [--loop] path preset directLevel " 363 "sendLevel\n", programName); 364 fprintf(stdout, "Example: \"%s /sdcard/my.mp3 6 -2000 0\" \n", programName); 365 exit(EXIT_FAILURE); 366 } 367 368 SLEngineOption EngineOption[] = { 369 {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE} 370 }; 371 372 result = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL); 373 ExitOnError(result); 374 375 /* Realizing the SL Engine in synchronous mode. */ 376 result = (*sl)->Realize(sl, SL_BOOLEAN_FALSE); 377 ExitOnError(result); 378 379 // intentionally not checking that levels are of correct value 380 TestSendToPresetReverb(sl, argv[i], atoi(argv[i+1]), (SLmillibel)atoi(argv[i+2]), 381 (SLmillibel)atoi(argv[i+3]), alwaysOn, useFd, loop); 382 383 /* Shutdown OpenSL ES */ 384 (*sl)->Destroy(sl); 385 386 return EXIT_SUCCESS; 387 } 388