/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This is an OpenAL backend for Android using the native audio APIs based on * OpenSL ES 1.0.1. It is based on source code for the native-audio sample app * bundled with NDK. */ #include "config.h" #include #include "alMain.h" #include "AL/al.h" #include "AL/alc.h" #include #if 1 #include #else extern SLAPIENTRY const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE; struct SLAndroidSimpleBufferQueueItf_; typedef const struct SLAndroidSimpleBufferQueueItf_ * const * SLAndroidSimpleBufferQueueItf; typedef void (*slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller, void *pContext); typedef struct SLAndroidSimpleBufferQueueState_ { SLuint32 count; SLuint32 index; } SLAndroidSimpleBufferQueueState; struct SLAndroidSimpleBufferQueueItf_ { SLresult (*Enqueue) ( SLAndroidSimpleBufferQueueItf self, const void *pBuffer, SLuint32 size ); SLresult (*Clear) ( SLAndroidSimpleBufferQueueItf self ); SLresult (*GetState) ( SLAndroidSimpleBufferQueueItf self, SLAndroidSimpleBufferQueueState *pState ); SLresult (*RegisterCallback) ( SLAndroidSimpleBufferQueueItf self, slAndroidSimpleBufferQueueCallback callback, void* pContext ); }; #define SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE ((SLuint32) 0x800007BD) typedef struct SLDataLocator_AndroidSimpleBufferQueue { SLuint32 locatorType; SLuint32 numBuffers; } SLDataLocator_AndroidSimpleBufferQueue; #endif /* Helper macros */ #define SLObjectItf_Realize(a,b) ((*(a))->Realize((a),(b))) #define SLObjectItf_GetInterface(a,b,c) ((*(a))->GetInterface((a),(b),(c))) #define SLObjectItf_Destroy(a) ((*(a))->Destroy((a))) #define SLEngineItf_CreateOutputMix(a,b,c,d,e) ((*(a))->CreateOutputMix((a),(b),(c),(d),(e))) #define SLEngineItf_CreateAudioPlayer(a,b,c,d,e,f,g) ((*(a))->CreateAudioPlayer((a),(b),(c),(d),(e),(f),(g))) #define SLPlayItf_SetPlayState(a,b) ((*(a))->SetPlayState((a),(b))) typedef struct { /* engine interfaces */ SLObjectItf engineObject; SLEngineItf engine; /* output mix interfaces */ SLObjectItf outputMix; /* buffer queue player interfaces */ SLObjectItf bufferQueueObject; void *buffer; ALuint bufferSize; ALuint frameSize; } osl_data; static const ALCchar opensl_device[] = "OpenSL"; static const char *res_str(SLresult result) { switch(result) { case SL_RESULT_SUCCESS: return "Success"; case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated"; case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid"; case SL_RESULT_MEMORY_FAILURE: return "Memory failure"; case SL_RESULT_RESOURCE_ERROR: return "Resource error"; case SL_RESULT_RESOURCE_LOST: return "Resource lost"; case SL_RESULT_IO_ERROR: return "I/O error"; case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient"; case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted"; case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported"; case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found"; case SL_RESULT_PERMISSION_DENIED: return "Permission denied"; case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported"; case SL_RESULT_INTERNAL_ERROR: return "Internal error"; case SL_RESULT_UNKNOWN_ERROR: return "Unknown error"; case SL_RESULT_OPERATION_ABORTED: return "Operation aborted"; case SL_RESULT_CONTROL_LOST: return "Control lost"; case SL_RESULT_READONLY: return "ReadOnly"; case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported"; case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible"; } return "Unknown error code"; } #define PRINTERR(x, s) do { \ if((x) != SL_RESULT_SUCCESS) \ AL_PRINT("%s: %s\n", (s), res_str((x))); \ } while(0) /* this callback handler is called every time a buffer finishes playing */ static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) { ALCdevice *Device = context; osl_data *data = Device->ExtraData; aluMixData(Device, data->buffer, data->bufferSize/data->frameSize); (*bq)->Enqueue(bq, data->buffer, data->bufferSize); } static ALCboolean opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName) { osl_data *data = NULL; SLresult result; if(!deviceName) deviceName = opensl_device; else if(strcmp(deviceName, opensl_device) != 0) return ALC_FALSE; data = calloc(1, sizeof(*data)); if(!data) { alcSetError(Device, ALC_OUT_OF_MEMORY); return ALC_FALSE; } // create engine result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL); PRINTERR(result, "slCreateEngine"); if(SL_RESULT_SUCCESS == result) { result = SLObjectItf_Realize(data->engineObject, SL_BOOLEAN_FALSE); PRINTERR(result, "engine->Realize"); } if(SL_RESULT_SUCCESS == result) { result = SLObjectItf_GetInterface(data->engineObject, SL_IID_ENGINE, &data->engine); PRINTERR(result, "engine->GetInterface"); } if(SL_RESULT_SUCCESS == result) { result = SLEngineItf_CreateOutputMix(data->engine, &data->outputMix, 0, NULL, NULL); PRINTERR(result, "engine->CreateOutputMix"); } if(SL_RESULT_SUCCESS == result) { result = SLObjectItf_Realize(data->outputMix, SL_BOOLEAN_FALSE); PRINTERR(result, "outputMix->Realize"); } if(SL_RESULT_SUCCESS != result) { if(data->outputMix != NULL) SLObjectItf_Destroy(data->outputMix); data->outputMix = NULL; if(data->engineObject != NULL) SLObjectItf_Destroy(data->engineObject); data->engineObject = NULL; data->engine = NULL; free(data); return ALC_FALSE; } Device->szDeviceName = strdup(deviceName); Device->ExtraData = data; return ALC_TRUE; } static void opensl_close_playback(ALCdevice *Device) { osl_data *data = Device->ExtraData; SLObjectItf_Destroy(data->outputMix); data->outputMix = NULL; SLObjectItf_Destroy(data->engineObject); data->engineObject = NULL; data->engine = NULL; free(data); Device->ExtraData = NULL; } static ALCboolean opensl_reset_playback(ALCdevice *Device) { osl_data *data = Device->ExtraData; SLDataLocator_AndroidSimpleBufferQueue loc_bufq; SLAndroidSimpleBufferQueueItf bufferQueue; SLDataLocator_OutputMix loc_outmix; SLDataFormat_PCM format_pcm; SLDataSource audioSrc; SLDataSink audioSnk; SLPlayItf player; SLInterfaceID id; SLboolean req; SLresult result; ALuint i; Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency; Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2; Device->NumUpdates = 2; Device->Frequency = 44100; Device->FmtChans = DevFmtStereo; Device->FmtType = DevFmtShort; SetDefaultWFXChannelOrder(Device); id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; req = SL_BOOLEAN_TRUE; loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; loc_bufq.numBuffers = Device->NumUpdates; format_pcm.formatType = SL_DATAFORMAT_PCM; format_pcm.numChannels = 2; format_pcm.samplesPerSec = SL_SAMPLINGRATE_44_1; format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; format_pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; format_pcm.endianness = SL_BYTEORDER_NATIVE; audioSrc.pLocator = &loc_bufq; audioSrc.pFormat = &format_pcm; loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; loc_outmix.outputMix = data->outputMix; audioSnk.pLocator = &loc_outmix; audioSnk.pFormat = NULL; result = SLEngineItf_CreateAudioPlayer(data->engine, &data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req); PRINTERR(result, "engine->CreateAudioPlayer"); if(SL_RESULT_SUCCESS == result) { result = SLObjectItf_Realize(data->bufferQueueObject, SL_BOOLEAN_FALSE); PRINTERR(result, "bufferQueue->Realize"); } if(SL_RESULT_SUCCESS == result) { result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_BUFFERQUEUE, &bufferQueue); PRINTERR(result, "bufferQueue->GetInterface"); } if(SL_RESULT_SUCCESS == result) { result = (*bufferQueue)->RegisterCallback(bufferQueue, opensl_callback, Device); PRINTERR(result, "bufferQueue->RegisterCallback"); } if(SL_RESULT_SUCCESS == result) { data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); data->bufferSize = Device->UpdateSize * data->frameSize; data->buffer = calloc(1, data->bufferSize); if(!data->buffer) { result = SL_RESULT_MEMORY_FAILURE; PRINTERR(result, "calloc"); } } /* enqueue the first buffer to kick off the callbacks */ for(i = 0;i < Device->NumUpdates;i++) { if(SL_RESULT_SUCCESS == result) { result = (*bufferQueue)->Enqueue(bufferQueue, data->buffer, data->bufferSize); PRINTERR(result, "bufferQueue->Enqueue"); } } if(SL_RESULT_SUCCESS == result) { result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_PLAY, &player); PRINTERR(result, "bufferQueue->GetInterface"); } if(SL_RESULT_SUCCESS == result) { result = SLPlayItf_SetPlayState(player, SL_PLAYSTATE_PLAYING); PRINTERR(result, "player->SetPlayState"); } if(SL_RESULT_SUCCESS != result) { if(data->bufferQueueObject != NULL) SLObjectItf_Destroy(data->bufferQueueObject); data->bufferQueueObject = NULL; free(data->buffer); data->buffer = NULL; data->bufferSize = 0; return ALC_FALSE; } return ALC_TRUE; } static void opensl_stop_playback(ALCdevice *Device) { osl_data *data = Device->ExtraData; if(data->bufferQueueObject != NULL) SLObjectItf_Destroy(data->bufferQueueObject); data->bufferQueueObject = NULL; free(data->buffer); data->buffer = NULL; data->bufferSize = 0; } static ALCboolean opensl_open_capture(ALCdevice *Device, const ALCchar *deviceName) { return ALC_FALSE; (void)Device; (void)deviceName; } static const BackendFuncs opensl_funcs = { opensl_open_playback, opensl_close_playback, opensl_reset_playback, opensl_stop_playback, opensl_open_capture, NULL, NULL, NULL, NULL, NULL }; void alc_opensl_init(BackendFuncs *func_list) { *func_list = opensl_funcs; } void alc_opensl_deinit(void) { } void alc_opensl_probe(int type) { switch(type) { case DEVICE_PROBE: AppendDeviceList(opensl_device); break; case ALL_DEVICE_PROBE: AppendAllDeviceList(opensl_device); break; default: break; } }