/* * Copyright (C) 2010 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. */ #include "sles_allinclusive.h" #include "android_prompts.h" #include "channels.h" #include #include #include #include #include #define KEY_RECORDING_SOURCE_PARAMSIZE sizeof(SLuint32) #define KEY_RECORDING_PRESET_PARAMSIZE sizeof(SLuint32) #define KEY_PERFORMANCE_MODE_PARAMSIZE sizeof(SLuint32) using android::content::AttributionSourceState; //----------------------------------------------------------------------------- // Internal utility functions //---------------------------- SLresult audioRecorder_setPreset(CAudioRecorder* ar, SLuint32 recordPreset) { SLresult result = SL_RESULT_SUCCESS; audio_source_t newRecordSource = AUDIO_SOURCE_DEFAULT; switch (recordPreset) { case SL_ANDROID_RECORDING_PRESET_GENERIC: newRecordSource = AUDIO_SOURCE_DEFAULT; break; case SL_ANDROID_RECORDING_PRESET_CAMCORDER: newRecordSource = AUDIO_SOURCE_CAMCORDER; break; case SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION: newRecordSource = AUDIO_SOURCE_VOICE_RECOGNITION; break; case SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION: newRecordSource = AUDIO_SOURCE_VOICE_COMMUNICATION; break; case SL_ANDROID_RECORDING_PRESET_UNPROCESSED: newRecordSource = AUDIO_SOURCE_UNPROCESSED; break; case SL_ANDROID_RECORDING_PRESET_NONE: // it is an error to set preset "none" default: SL_LOGE(ERROR_RECORDERPRESET_SET_UNKNOWN_PRESET); result = SL_RESULT_PARAMETER_INVALID; } // recording preset needs to be set before the object is realized // (ap->mAudioRecord is supposed to be 0 until then) if (SL_OBJECT_STATE_UNREALIZED != ar->mObject.mState) { SL_LOGE(ERROR_RECORDERPRESET_REALIZED); result = SL_RESULT_PRECONDITIONS_VIOLATED; } else { ar->mRecordSource = newRecordSource; } return result; } //----------------------------------------------------------------------------- SLresult audioRecorder_setPerformanceMode(CAudioRecorder* ar, SLuint32 mode) { SLresult result = SL_RESULT_SUCCESS; SL_LOGV("performance mode set to %d", mode); SLuint32 perfMode = ANDROID_PERFORMANCE_MODE_DEFAULT; switch (mode) { case SL_ANDROID_PERFORMANCE_LATENCY: perfMode = ANDROID_PERFORMANCE_MODE_LATENCY; break; case SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS: perfMode = ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS; break; case SL_ANDROID_PERFORMANCE_NONE: perfMode = ANDROID_PERFORMANCE_MODE_NONE; break; case SL_ANDROID_PERFORMANCE_POWER_SAVING: perfMode = ANDROID_PERFORMANCE_MODE_POWER_SAVING; break; default: SL_LOGE(ERROR_CONFIG_PERF_MODE_UNKNOWN); result = SL_RESULT_PARAMETER_INVALID; break; } // performance mode needs to be set before the object is realized // (ar->mAudioRecord is supposed to be NULL until then) if (SL_OBJECT_STATE_UNREALIZED != ar->mObject.mState) { SL_LOGE(ERROR_CONFIG_PERF_MODE_REALIZED); result = SL_RESULT_PRECONDITIONS_VIOLATED; } else { ar->mPerformanceMode = perfMode; } return result; } SLresult audioRecorder_getPreset(CAudioRecorder* ar, SLuint32* pPreset) { SLresult result = SL_RESULT_SUCCESS; switch (ar->mRecordSource) { case AUDIO_SOURCE_DEFAULT: case AUDIO_SOURCE_MIC: *pPreset = SL_ANDROID_RECORDING_PRESET_GENERIC; break; case AUDIO_SOURCE_VOICE_UPLINK: case AUDIO_SOURCE_VOICE_DOWNLINK: case AUDIO_SOURCE_VOICE_CALL: *pPreset = SL_ANDROID_RECORDING_PRESET_NONE; break; case AUDIO_SOURCE_VOICE_RECOGNITION: *pPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; break; case AUDIO_SOURCE_CAMCORDER: *pPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER; break; case AUDIO_SOURCE_VOICE_COMMUNICATION: *pPreset = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; break; case AUDIO_SOURCE_UNPROCESSED: *pPreset = SL_ANDROID_RECORDING_PRESET_UNPROCESSED; break; default: *pPreset = SL_ANDROID_RECORDING_PRESET_NONE; result = SL_RESULT_INTERNAL_ERROR; break; } return result; } //----------------------------------------------------------------------------- SLresult audioRecorder_getPerformanceMode(CAudioRecorder* ar, SLuint32 *pMode) { SLresult result = SL_RESULT_SUCCESS; switch (ar->mPerformanceMode) { case ANDROID_PERFORMANCE_MODE_LATENCY: *pMode = SL_ANDROID_PERFORMANCE_LATENCY; break; case ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS: *pMode = SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS; break; case ANDROID_PERFORMANCE_MODE_NONE: *pMode = SL_ANDROID_PERFORMANCE_NONE; break; case ANDROID_PERFORMANCE_MODE_POWER_SAVING: *pMode = SL_ANDROID_PERFORMANCE_POWER_SAVING; break; default: result = SL_RESULT_INTERNAL_ERROR; *pMode = SL_ANDROID_PERFORMANCE_LATENCY; break; } return result; } void audioRecorder_handleNewPos_lockRecord(CAudioRecorder* ar) { //SL_LOGV("received event EVENT_NEW_POS from AudioRecord"); slRecordCallback callback = NULL; void* callbackPContext = NULL; interface_lock_shared(&ar->mRecord); callback = ar->mRecord.mCallback; callbackPContext = ar->mRecord.mContext; interface_unlock_shared(&ar->mRecord); if (NULL != callback) { // getting this event implies SL_RECORDEVENT_HEADATNEWPOS was set in the event mask (*callback)(&ar->mRecord.mItf, callbackPContext, SL_RECORDEVENT_HEADATNEWPOS); } } void audioRecorder_handleMarker_lockRecord(CAudioRecorder* ar) { //SL_LOGV("received event EVENT_MARKER from AudioRecord"); slRecordCallback callback = NULL; void* callbackPContext = NULL; interface_lock_shared(&ar->mRecord); callback = ar->mRecord.mCallback; callbackPContext = ar->mRecord.mContext; interface_unlock_shared(&ar->mRecord); if (NULL != callback) { // getting this event implies SL_RECORDEVENT_HEADATMARKER was set in the event mask (*callback)(&ar->mRecord.mItf, callbackPContext, SL_RECORDEVENT_HEADATMARKER); } } void audioRecorder_handleOverrun_lockRecord(CAudioRecorder* ar) { //SL_LOGV("received event EVENT_OVERRUN from AudioRecord"); slRecordCallback callback = NULL; void* callbackPContext = NULL; interface_lock_shared(&ar->mRecord); if (ar->mRecord.mCallbackEventsMask & SL_RECORDEVENT_HEADSTALLED) { callback = ar->mRecord.mCallback; callbackPContext = ar->mRecord.mContext; } interface_unlock_shared(&ar->mRecord); if (NULL != callback) { (*callback)(&ar->mRecord.mItf, callbackPContext, SL_RECORDEVENT_HEADSTALLED); } } //----------------------------------------------------------------------------- SLresult android_audioRecorder_checkSourceSink(CAudioRecorder* ar) { const SLDataSource *pAudioSrc = &ar->mDataSource.u.mSource; const SLDataSink *pAudioSnk = &ar->mDataSink.u.mSink; const SLuint32 sinkLocatorType = *(SLuint32 *)pAudioSnk->pLocator; const SLuint32 sinkFormatType = *(SLuint32 *)pAudioSnk->pFormat; const SLuint32 *df_representation = NULL; // pointer to representation field, if it exists // sink must be an Android simple buffer queue with PCM data format switch (sinkLocatorType) { case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE: { switch (sinkFormatType) { case SL_ANDROID_DATAFORMAT_PCM_EX: { const SLAndroidDataFormat_PCM_EX *df_pcm = (SLAndroidDataFormat_PCM_EX *) pAudioSnk->pFormat; // checkDataFormat() already checked representation df_representation = &df_pcm->representation; } // SL_ANDROID_DATAFORMAT_PCM_EX - fall through to next test. FALLTHROUGH_INTENDED; case SL_DATAFORMAT_PCM: { const SLDataFormat_PCM *df_pcm = (const SLDataFormat_PCM *) pAudioSnk->pFormat; // checkDataFormat already checked sample rate, channels, and mask ar->mNumChannels = df_pcm->numChannels; if (df_pcm->endianness != ar->mObject.mEngine->mEngine.mNativeEndianness) { SL_LOGE("Cannot create audio recorder: unsupported byte order %u", df_pcm->endianness); return SL_RESULT_CONTENT_UNSUPPORTED; } ar->mSampleRateMilliHz = df_pcm->samplesPerSec; // Note: bad field name in SL ES SL_LOGV("AudioRecorder requested sample rate = %u mHz, %u channel(s)", ar->mSampleRateMilliHz, ar->mNumChannels); // we don't support container size != sample depth if (df_pcm->containerSize != df_pcm->bitsPerSample) { SL_LOGE("Cannot create audio recorder: unsupported container size %u bits for " "sample depth %u bits", df_pcm->containerSize, (SLuint32)df_pcm->bitsPerSample); return SL_RESULT_CONTENT_UNSUPPORTED; } } break; default: SL_LOGE(ERROR_RECORDER_SINK_FORMAT_MUST_BE_PCM); return SL_RESULT_PARAMETER_INVALID; } // switch (sourceFormatType) } break; // case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE default: SL_LOGE(ERROR_RECORDER_SINK_MUST_BE_ANDROIDSIMPLEBUFFERQUEUE); return SL_RESULT_PARAMETER_INVALID; } // switch (sourceLocatorType) // Source check: // only input device sources are supported // check it's an IO device if (SL_DATALOCATOR_IODEVICE != *(SLuint32 *)pAudioSrc->pLocator) { SL_LOGE(ERROR_RECORDER_SOURCE_MUST_BE_IODEVICE); return SL_RESULT_PARAMETER_INVALID; } else { // check it's an input device SLDataLocator_IODevice *dl_iod = (SLDataLocator_IODevice *) pAudioSrc->pLocator; if (SL_IODEVICE_AUDIOINPUT != dl_iod->deviceType) { SL_LOGE(ERROR_RECORDER_IODEVICE_MUST_BE_AUDIOINPUT); return SL_RESULT_PARAMETER_INVALID; } // check it's the default input device, others aren't supported here if (SL_DEFAULTDEVICEID_AUDIOINPUT != dl_iod->deviceID) { SL_LOGE(ERROR_RECORDER_INPUT_ID_MUST_BE_DEFAULT); return SL_RESULT_PARAMETER_INVALID; } } return SL_RESULT_SUCCESS; } //----------------------------------------------------------------------------- size_t audioRecorder_handleMoreData_lockRecord(CAudioRecorder* ar, const android::AudioRecord::Buffer& buffer) { //SL_LOGV("audioRecorder_callback(%d, %p, %p) entering", event, user, info); void * callbackPContext = NULL; size_t bytesRead = 0; slBufferQueueCallback callback = NULL; // push data to the buffer queue interface_lock_exclusive(&ar->mBufferQueue); if (ar->mBufferQueue.mState.count != 0) { assert(ar->mBufferQueue.mFront != ar->mBufferQueue.mRear); BufferHeader *oldFront = ar->mBufferQueue.mFront; BufferHeader *newFront = &oldFront[1]; size_t availSink = oldFront->mSize - ar->mBufferQueue.mSizeConsumed; size_t availSource = buffer.size(); size_t bytesToCopy = availSink < availSource ? availSink : availSource; void *pDest = (char *)oldFront->mBuffer + ar->mBufferQueue.mSizeConsumed; memcpy(pDest, buffer.data(), bytesToCopy); bytesRead = bytesToCopy; if (bytesToCopy < availSink) { // can't consume the whole or rest of the buffer in one shot ar->mBufferQueue.mSizeConsumed += availSource; } else { // finish pushing the buffer or push the buffer in one shot ar->mBufferQueue.mSizeConsumed = 0; if (newFront == &ar->mBufferQueue.mArray[ar->mBufferQueue.mNumBuffers + 1]) { newFront = ar->mBufferQueue.mArray; } ar->mBufferQueue.mFront = newFront; ar->mBufferQueue.mState.count--; ar->mBufferQueue.mState.playIndex++; // data has been copied to the buffer, and the buffer queue state has been updated // we will notify the client if applicable callback = ar->mBufferQueue.mCallback; // save callback data callbackPContext = ar->mBufferQueue.mContext; } } interface_unlock_exclusive(&ar->mBufferQueue); // notify client if (NULL != callback) { (*callback)(&ar->mBufferQueue.mItf, callbackPContext); } return bytesRead; } //----------------------------------------------------------------------------- SLresult android_audioRecorder_create(CAudioRecorder* ar) { SL_LOGV("android_audioRecorder_create(%p) entering", ar); const SLDataSource *pAudioSrc = &ar->mDataSource.u.mSource; const SLDataSink *pAudioSnk = &ar->mDataSink.u.mSink; SLresult result = SL_RESULT_SUCCESS; const SLuint32 sourceLocatorType = *(SLuint32 *)pAudioSrc->pLocator; const SLuint32 sinkLocatorType = *(SLuint32 *)pAudioSnk->pLocator; // the following platform-independent fields have been initialized in CreateAudioRecorder() // ar->mNumChannels // ar->mSampleRateMilliHz if ((SL_DATALOCATOR_IODEVICE == sourceLocatorType) && (SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE == sinkLocatorType)) { // microphone to simple buffer queue ar->mAndroidObjType = AUDIORECORDER_FROM_MIC_TO_PCM_BUFFERQUEUE; ar->mAudioRecord.clear(); ar->mCallbackHandle.clear(); ar->mCallbackProtector = new android::CallbackProtector(); ar->mRecordSource = AUDIO_SOURCE_DEFAULT; ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_DEFAULT; } else { result = SL_RESULT_CONTENT_UNSUPPORTED; } return result; } //----------------------------------------------------------------------------- SLresult android_audioRecorder_setConfig(CAudioRecorder* ar, const SLchar *configKey, const void *pConfigValue, SLuint32 valueSize) { SLresult result; assert(NULL != ar && NULL != configKey && NULL != pConfigValue); if (strcmp((const char*)configKey, (const char*)SL_ANDROID_KEY_RECORDING_PRESET) == 0) { // recording preset if (KEY_RECORDING_PRESET_PARAMSIZE > valueSize) { SL_LOGE(ERROR_CONFIG_VALUESIZE_TOO_LOW); result = SL_RESULT_BUFFER_INSUFFICIENT; } else { result = audioRecorder_setPreset(ar, *(SLuint32*)pConfigValue); } } else if (strcmp((const char*)configKey, (const char*)SL_ANDROID_KEY_PERFORMANCE_MODE) == 0) { // performance mode if (KEY_PERFORMANCE_MODE_PARAMSIZE > valueSize) { SL_LOGE(ERROR_CONFIG_VALUESIZE_TOO_LOW); result = SL_RESULT_BUFFER_INSUFFICIENT; } else { result = audioRecorder_setPerformanceMode(ar, *(SLuint32*)pConfigValue); } } else { SL_LOGE(ERROR_CONFIG_UNKNOWN_KEY); result = SL_RESULT_PARAMETER_INVALID; } return result; } //----------------------------------------------------------------------------- SLresult android_audioRecorder_getConfig(CAudioRecorder* ar, const SLchar *configKey, SLuint32* pValueSize, void *pConfigValue) { SLresult result; assert(NULL != ar && NULL != configKey && NULL != pValueSize); if (strcmp((const char*)configKey, (const char*)SL_ANDROID_KEY_RECORDING_PRESET) == 0) { // recording preset if (NULL == pConfigValue) { result = SL_RESULT_SUCCESS; } else if (KEY_RECORDING_PRESET_PARAMSIZE > *pValueSize) { SL_LOGE(ERROR_CONFIG_VALUESIZE_TOO_LOW); result = SL_RESULT_BUFFER_INSUFFICIENT; } else { result = audioRecorder_getPreset(ar, (SLuint32*)pConfigValue); } *pValueSize = KEY_RECORDING_PRESET_PARAMSIZE; } else if (strcmp((const char*)configKey, (const char*)SL_ANDROID_KEY_PERFORMANCE_MODE) == 0) { // performance mode if (NULL == pConfigValue) { result = SL_RESULT_SUCCESS; } else if (KEY_PERFORMANCE_MODE_PARAMSIZE > *pValueSize) { SL_LOGE(ERROR_CONFIG_VALUESIZE_TOO_LOW); result = SL_RESULT_BUFFER_INSUFFICIENT; } else { result = audioRecorder_getPerformanceMode(ar, (SLuint32*)pConfigValue); } *pValueSize = KEY_PERFORMANCE_MODE_PARAMSIZE; } else { SL_LOGE(ERROR_CONFIG_UNKNOWN_KEY); result = SL_RESULT_PARAMETER_INVALID; } return result; } // Called from android_audioRecorder_realize for a PCM buffer queue recorder before creating the // AudioRecord to determine which performance modes are allowed based on effect interfaces present static void checkAndSetPerformanceModePre(CAudioRecorder* ar) { SLuint32 allowedModes = ANDROID_PERFORMANCE_MODE_ALL; assert(ar->mAndroidObjType == AUDIORECORDER_FROM_MIC_TO_PCM_BUFFERQUEUE); // no need to check the buffer queue size, application side // double-buffering (and more) is not a requirement for using fast tracks // Check a denylist of interfaces that are incompatible with fast tracks. // The alternative, to check a allowlist of compatible interfaces, is // more maintainable but is too slow. As a compromise, in a debug build // we use both methods and warn if they produce different results. // In release builds, we only use the denylist method. // If a denylisted interface is added after realization using // DynamicInterfaceManagement::AddInterface, // then this won't be detected but the interface will be ineffective. static const unsigned denylist[] = { MPH_ANDROIDACOUSTICECHOCANCELLATION, MPH_ANDROIDAUTOMATICGAINCONTROL, MPH_ANDROIDNOISESUPPRESSION, MPH_ANDROIDEFFECT, // FIXME The problem with a denylist is remembering to add new interfaces here }; for (unsigned i = 0; i < sizeof(denylist)/sizeof(denylist[0]); ++i) { if (IsInterfaceInitialized(&ar->mObject, denylist[i])) { uint32_t flags = 0; allowedModes &= ~ANDROID_PERFORMANCE_MODE_LATENCY; // if generic effect interface is used we don't know which effect will be used and // disable all low latency performance modes if (denylist[i] != MPH_ANDROIDEFFECT) { switch (denylist[i]) { case MPH_ANDROIDACOUSTICECHOCANCELLATION: SL_LOGV("checkAndSetPerformanceModePre found AEC name %s", ar->mAcousticEchoCancellation.mAECDescriptor.name); flags = ar->mAcousticEchoCancellation.mAECDescriptor.flags; break; case MPH_ANDROIDAUTOMATICGAINCONTROL: SL_LOGV("checkAndSetPerformanceModePre found AGC name %s", ar->mAutomaticGainControl.mAGCDescriptor.name); flags = ar->mAutomaticGainControl.mAGCDescriptor.flags; break; case MPH_ANDROIDNOISESUPPRESSION: SL_LOGV("checkAndSetPerformanceModePre found NS name %s", ar->mNoiseSuppression.mNSDescriptor.name); flags = ar->mNoiseSuppression.mNSDescriptor.flags; break; default: break; } } if ((flags & EFFECT_FLAG_HW_ACC_TUNNEL) == 0) { allowedModes &= ~ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS; break; } } } #if LOG_NDEBUG == 0 bool denylistResult = ( (allowedModes & (ANDROID_PERFORMANCE_MODE_LATENCY|ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS)) != 0); bool allowlistResult = true; static const unsigned allowlist[] = { MPH_BUFFERQUEUE, MPH_DYNAMICINTERFACEMANAGEMENT, MPH_OBJECT, MPH_RECORD, MPH_ANDROIDCONFIGURATION, MPH_ANDROIDSIMPLEBUFFERQUEUE, }; for (unsigned mph = MPH_MIN; mph < MPH_MAX; ++mph) { for (unsigned i = 0; i < sizeof(allowlist)/sizeof(allowlist[0]); ++i) { if (mph == allowlist[i]) { goto compatible; } } if (IsInterfaceInitialized(&ar->mObject, mph)) { allowlistResult = false; break; } compatible: ; } if (allowlistResult != denylistResult) { SL_LOGW("allowlistResult != denylistResult"); } #endif if (ar->mPerformanceMode == ANDROID_PERFORMANCE_MODE_LATENCY) { if ((allowedModes & ANDROID_PERFORMANCE_MODE_LATENCY) == 0) { ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS; } } if (ar->mPerformanceMode == ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS) { if ((allowedModes & ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS) == 0) { ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_NONE; } } } // Called from android_audioRecorder_realize for a PCM buffer queue recorder after creating the // AudioRecord to adjust performance mode based on actual input flags static void checkAndSetPerformanceModePost(CAudioRecorder* ar) { audio_input_flags_t flags = ar->mAudioRecord->getFlags(); switch (ar->mPerformanceMode) { case ANDROID_PERFORMANCE_MODE_LATENCY: if ((flags & (AUDIO_INPUT_FLAG_FAST | AUDIO_INPUT_FLAG_RAW)) == (AUDIO_INPUT_FLAG_FAST | AUDIO_INPUT_FLAG_RAW)) { break; } ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS; FALLTHROUGH_INTENDED; case ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS: if ((flags & AUDIO_INPUT_FLAG_FAST) == 0) { ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_NONE; } break; case ANDROID_PERFORMANCE_MODE_NONE: default: break; } } //----------------------------------------------------------------------------- SLresult android_audioRecorder_realize(CAudioRecorder* ar, SLboolean async) { SL_LOGV("android_audioRecorder_realize(%p) entering", ar); SLresult result = SL_RESULT_SUCCESS; // already checked in created and checkSourceSink assert(ar->mDataSink.mLocator.mLocatorType == SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE); const SLDataFormat_PCM *df_pcm = &ar->mDataSink.mFormat.mPCM; // the following platform-independent fields have been initialized in CreateAudioRecorder() // ar->mNumChannels // ar->mSampleRateMilliHz uint32_t sampleRate = sles_to_android_sampleRate(df_pcm->samplesPerSec); checkAndSetPerformanceModePre(ar); audio_input_flags_t policy; switch (ar->mPerformanceMode) { case ANDROID_PERFORMANCE_MODE_NONE: case ANDROID_PERFORMANCE_MODE_POWER_SAVING: policy = AUDIO_INPUT_FLAG_NONE; break; case ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS: policy = AUDIO_INPUT_FLAG_FAST; break; case ANDROID_PERFORMANCE_MODE_LATENCY: default: policy = (audio_input_flags_t)(AUDIO_INPUT_FLAG_FAST | AUDIO_INPUT_FLAG_RAW); break; } SL_LOGV("Audio Record format: %dch(0x%x), %dbit, %dKHz", df_pcm->numChannels, df_pcm->channelMask, df_pcm->bitsPerSample, df_pcm->samplesPerSec / 1000000); // note that df_pcm->channelMask has already been validated during object creation. audio_channel_mask_t channelMask = sles_to_audio_input_channel_mask(df_pcm->channelMask); // To maintain backward compatibility with previous releases, ignore // channel masks that are not indexed. if (channelMask == AUDIO_CHANNEL_INVALID || audio_channel_mask_get_representation(channelMask) == AUDIO_CHANNEL_REPRESENTATION_POSITION) { channelMask = audio_channel_in_mask_from_count(df_pcm->numChannels); SL_LOGI("Emulating old channel mask behavior " "(ignoring positional mask %#x, using default mask %#x based on " "channel count of %d)", df_pcm->channelMask, channelMask, df_pcm->numChannels); } SL_LOGV("SLES channel mask %#x converted to Android mask %#x", df_pcm->channelMask, channelMask); // TODO b/182392769: use attribution source util AttributionSourceState attributionSource; attributionSource.uid = VALUE_OR_FATAL(android::legacy2aidl_uid_t_int32_t(getuid())); attributionSource.pid = VALUE_OR_FATAL(android::legacy2aidl_pid_t_int32_t(getpid())); attributionSource.token = android::sp::make(); ar->mCallbackHandle = android::sp::make(ar); // initialize platform-specific CAudioRecorder fields ar->mAudioRecord = new android::AudioRecord( ar->mRecordSource, // source sampleRate, // sample rate in Hertz sles_to_android_sampleFormat(df_pcm), // format channelMask, // channel mask attributionSource, 0, // frameCount ar->mCallbackHandle, 0, // notificationFrames AUDIO_SESSION_ALLOCATE, android::AudioRecord::TRANSFER_CALLBACK, // transfer type policy); // audio_input_flags_t // Set it here so it can be logged by the destructor if the open failed. ar->mAudioRecord->setCallerName(ANDROID_OPENSLES_CALLER_NAME); android::status_t status = ar->mAudioRecord->initCheck(); if (android::NO_ERROR != status) { SL_LOGE("android_audioRecorder_realize(%p) error creating AudioRecord object; status %d", ar, status); // FIXME should return a more specific result depending on status result = SL_RESULT_CONTENT_UNSUPPORTED; ar->mAudioRecord.clear(); ar->mCallbackHandle.clear(); return result; } // update performance mode according to actual flags granted to AudioRecord checkAndSetPerformanceModePost(ar); // If there is a JavaAudioRoutingProxy associated with this recorder, hook it up... JNIEnv* j_env = NULL; jclass clsAudioRecord = NULL; jmethodID midRoutingProxy_connect = NULL; if (ar->mAndroidConfiguration.mRoutingProxy != NULL && (j_env = android::AndroidRuntime::getJNIEnv()) != NULL && (clsAudioRecord = j_env->FindClass("android/media/AudioRecord")) != NULL && (midRoutingProxy_connect = j_env->GetMethodID(clsAudioRecord, "deferred_connect", "(J)V")) != NULL) { j_env->ExceptionClear(); j_env->CallVoidMethod(ar->mAndroidConfiguration.mRoutingProxy, midRoutingProxy_connect, ar->mAudioRecord.get()); if (j_env->ExceptionCheck()) { SL_LOGE("Java exception releasing recorder routing object."); result = SL_RESULT_INTERNAL_ERROR; ar->mAudioRecord.clear(); ar->mCallbackHandle.clear(); return result; } } if (ar->mPerformanceMode != ANDROID_PERFORMANCE_MODE_LATENCY) { audio_session_t sessionId = ar->mAudioRecord->getSessionId(); // initialize AEC effect_descriptor_t *descriptor = &ar->mAcousticEchoCancellation.mAECDescriptor; if (memcmp(SL_IID_ANDROIDACOUSTICECHOCANCELLATION, &descriptor->type, sizeof(effect_uuid_t)) == 0) { if ((ar->mPerformanceMode != ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS) || (descriptor->flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { SL_LOGV("Need to initialize AEC for AudioRecorder=%p", ar); android_aec_init(sessionId, &ar->mAcousticEchoCancellation); } } // initialize AGC descriptor = &ar->mAutomaticGainControl.mAGCDescriptor; if (memcmp(SL_IID_ANDROIDAUTOMATICGAINCONTROL, &descriptor->type, sizeof(effect_uuid_t)) == 0) { if ((ar->mPerformanceMode != ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS) || (descriptor->flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { SL_LOGV("Need to initialize AGC for AudioRecorder=%p", ar); android_agc_init(sessionId, &ar->mAutomaticGainControl); } } // initialize NS descriptor = &ar->mNoiseSuppression.mNSDescriptor; if (memcmp(SL_IID_ANDROIDNOISESUPPRESSION, &descriptor->type, sizeof(effect_uuid_t)) == 0) { if ((ar->mPerformanceMode != ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS) || (descriptor->flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { SL_LOGV("Need to initialize NS for AudioRecorder=%p", ar); android_ns_init(sessionId, &ar->mNoiseSuppression); } } } return result; } //----------------------------------------------------------------------------- /** * Called with a lock on AudioRecorder, and blocks until safe to destroy */ void android_audioRecorder_preDestroy(CAudioRecorder* ar) { object_unlock_exclusive(&ar->mObject); if (ar->mCallbackProtector != 0) { ar->mCallbackProtector->requestCbExitAndWait(); } object_lock_exclusive(&ar->mObject); } //----------------------------------------------------------------------------- void android_audioRecorder_destroy(CAudioRecorder* ar) { SL_LOGV("android_audioRecorder_destroy(%p) entering", ar); if (ar->mAudioRecord != 0) { ar->mAudioRecord->stop(); ar->mAudioRecord.clear(); } // explicit destructor ar->mCallbackHandle.~sp(); ar->mAudioRecord.~sp(); ar->mCallbackProtector.~sp(); } //----------------------------------------------------------------------------- void android_audioRecorder_setRecordState(CAudioRecorder* ar, SLuint32 state) { SL_LOGV("android_audioRecorder_setRecordState(%p, %u) entering", ar, state); if (ar->mAudioRecord == 0) { return; } switch (state) { case SL_RECORDSTATE_STOPPED: ar->mAudioRecord->stop(); break; case SL_RECORDSTATE_PAUSED: // Note that pausing is treated like stop as this implementation only records to a buffer // queue, so there is no notion of destination being "opened" or "closed" (See description // of SL_RECORDSTATE in specification) ar->mAudioRecord->stop(); break; case SL_RECORDSTATE_RECORDING: ar->mAudioRecord->start(); break; default: break; } } //----------------------------------------------------------------------------- void android_audioRecorder_useRecordEventMask(CAudioRecorder *ar) { IRecord *pRecordItf = &ar->mRecord; SLuint32 eventFlags = pRecordItf->mCallbackEventsMask; if (ar->mAudioRecord == 0) { return; } if ((eventFlags & SL_RECORDEVENT_HEADATMARKER) && (pRecordItf->mMarkerPosition != 0)) { ar->mAudioRecord->setMarkerPosition((uint32_t)((((int64_t)pRecordItf->mMarkerPosition * sles_to_android_sampleRate(ar->mSampleRateMilliHz)))/1000)); } else { // clear marker ar->mAudioRecord->setMarkerPosition(0); } if (eventFlags & SL_RECORDEVENT_HEADATNEWPOS) { SL_LOGV("pos update period %d", pRecordItf->mPositionUpdatePeriod); ar->mAudioRecord->setPositionUpdatePeriod( (uint32_t)((((int64_t)pRecordItf->mPositionUpdatePeriod * sles_to_android_sampleRate(ar->mSampleRateMilliHz)))/1000)); } else { // clear periodic update ar->mAudioRecord->setPositionUpdatePeriod(0); } if (eventFlags & SL_RECORDEVENT_HEADATLIMIT) { // FIXME support SL_RECORDEVENT_HEADATLIMIT SL_LOGD("[ FIXME: IRecord_SetCallbackEventsMask(SL_RECORDEVENT_HEADATLIMIT) on an " "SL_OBJECTID_AUDIORECORDER to be implemented ]"); } if (eventFlags & SL_RECORDEVENT_HEADMOVING) { // FIXME support SL_RECORDEVENT_HEADMOVING SL_LOGD("[ FIXME: IRecord_SetCallbackEventsMask(SL_RECORDEVENT_HEADMOVING) on an " "SL_OBJECTID_AUDIORECORDER to be implemented ]"); } if (eventFlags & SL_RECORDEVENT_BUFFER_FULL) { // nothing to do for SL_RECORDEVENT_BUFFER_FULL since this will not be encountered on // recording to buffer queues } if (eventFlags & SL_RECORDEVENT_HEADSTALLED) { // nothing to do for SL_RECORDEVENT_HEADSTALLED, callback event will be checked against mask // when AudioRecord::EVENT_OVERRUN is encountered } } //----------------------------------------------------------------------------- void android_audioRecorder_getPosition(CAudioRecorder *ar, SLmillisecond *pPosMsec) { if ((NULL == ar) || (ar->mAudioRecord == 0)) { *pPosMsec = 0; } else { uint32_t positionInFrames; ar->mAudioRecord->getPosition(&positionInFrames); if (ar->mSampleRateMilliHz == UNKNOWN_SAMPLERATE) { *pPosMsec = 0; } else { *pPosMsec = ((int64_t)positionInFrames * 1000) / sles_to_android_sampleRate(ar->mSampleRateMilliHz); } } }