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