| /* |
| * Copyright 2022 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 "le_audio_utils.h" |
| |
| #include "bta/le_audio/content_control_id_keeper.h" |
| #include "gd/common/strings.h" |
| #include "le_audio_types.h" |
| #include "osi/include/log.h" |
| |
| using bluetooth::common::ToString; |
| using le_audio::types::AudioContexts; |
| using le_audio::types::LeAudioContextType; |
| |
| namespace le_audio { |
| namespace utils { |
| |
| /* The returned LeAudioContextType should have its entry in the |
| * AudioSetConfigurationProvider's ContextTypeToScenario mapping table. |
| * Otherwise the AudioSetConfigurationProvider will fall back |
| * to default scenario. |
| */ |
| LeAudioContextType AudioContentToLeAudioContext( |
| audio_content_type_t content_type, audio_usage_t usage) { |
| /* Check audio attribute usage of stream */ |
| switch (usage) { |
| case AUDIO_USAGE_MEDIA: |
| return LeAudioContextType::MEDIA; |
| case AUDIO_USAGE_ASSISTANT: |
| return LeAudioContextType::VOICEASSISTANTS; |
| case AUDIO_USAGE_VOICE_COMMUNICATION: |
| case AUDIO_USAGE_CALL_ASSISTANT: |
| return LeAudioContextType::CONVERSATIONAL; |
| case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: |
| if (content_type == AUDIO_CONTENT_TYPE_SPEECH) |
| return LeAudioContextType::CONVERSATIONAL; |
| else |
| return LeAudioContextType::MEDIA; |
| case AUDIO_USAGE_GAME: |
| return LeAudioContextType::GAME; |
| case AUDIO_USAGE_NOTIFICATION: |
| return LeAudioContextType::NOTIFICATIONS; |
| case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: |
| return LeAudioContextType::RINGTONE; |
| case AUDIO_USAGE_ALARM: |
| return LeAudioContextType::ALERTS; |
| case AUDIO_USAGE_EMERGENCY: |
| return LeAudioContextType::EMERGENCYALARM; |
| case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: |
| return LeAudioContextType::INSTRUCTIONAL; |
| case AUDIO_USAGE_ASSISTANCE_SONIFICATION: |
| return LeAudioContextType::SOUNDEFFECTS; |
| default: |
| break; |
| } |
| |
| return LeAudioContextType::MEDIA; |
| } |
| |
| static std::string usageToString(audio_usage_t usage) { |
| switch (usage) { |
| case AUDIO_USAGE_UNKNOWN: |
| return "USAGE_UNKNOWN"; |
| case AUDIO_USAGE_MEDIA: |
| return "USAGE_MEDIA"; |
| case AUDIO_USAGE_VOICE_COMMUNICATION: |
| return "USAGE_VOICE_COMMUNICATION"; |
| case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: |
| return "USAGE_VOICE_COMMUNICATION_SIGNALLING"; |
| case AUDIO_USAGE_ALARM: |
| return "USAGE_ALARM"; |
| case AUDIO_USAGE_NOTIFICATION: |
| return "USAGE_NOTIFICATION"; |
| case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: |
| return "USAGE_NOTIFICATION_TELEPHONY_RINGTONE"; |
| case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: |
| return "USAGE_NOTIFICATION_COMMUNICATION_REQUEST"; |
| case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: |
| return "USAGE_NOTIFICATION_COMMUNICATION_INSTANT"; |
| case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: |
| return "USAGE_NOTIFICATION_COMMUNICATION_DELAYED"; |
| case AUDIO_USAGE_NOTIFICATION_EVENT: |
| return "USAGE_NOTIFICATION_EVENT"; |
| case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: |
| return "USAGE_ASSISTANCE_ACCESSIBILITY"; |
| case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: |
| return "USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"; |
| case AUDIO_USAGE_ASSISTANCE_SONIFICATION: |
| return "USAGE_ASSISTANCE_SONIFICATION"; |
| case AUDIO_USAGE_GAME: |
| return "USAGE_GAME"; |
| case AUDIO_USAGE_ASSISTANT: |
| return "USAGE_ASSISTANT"; |
| case AUDIO_USAGE_CALL_ASSISTANT: |
| return "USAGE_CALL_ASSISTANT"; |
| case AUDIO_USAGE_EMERGENCY: |
| return "USAGE_EMERGENCY"; |
| case AUDIO_USAGE_SAFETY: |
| return "USAGE_SAFETY"; |
| case AUDIO_USAGE_VEHICLE_STATUS: |
| return "USAGE_VEHICLE_STATUS"; |
| case AUDIO_USAGE_ANNOUNCEMENT: |
| return "USAGE_ANNOUNCEMENT"; |
| default: |
| return "unknown usage "; |
| } |
| } |
| |
| static std::string contentTypeToString(audio_content_type_t content_type) { |
| switch (content_type) { |
| case AUDIO_CONTENT_TYPE_UNKNOWN: |
| return "CONTENT_TYPE_UNKNOWN"; |
| case AUDIO_CONTENT_TYPE_SPEECH: |
| return "CONTENT_TYPE_SPEECH"; |
| case AUDIO_CONTENT_TYPE_MUSIC: |
| return "CONTENT_TYPE_MUSIC"; |
| case AUDIO_CONTENT_TYPE_MOVIE: |
| return "CONTENT_TYPE_MOVIE"; |
| case AUDIO_CONTENT_TYPE_SONIFICATION: |
| return "CONTENT_TYPE_SONIFICATION"; |
| default: |
| return "unknown content type "; |
| } |
| } |
| |
| static const char* audioSourceToStr(audio_source_t source) { |
| const char* strArr[] = { |
| "AUDIO_SOURCE_DEFAULT", "AUDIO_SOURCE_MIC", |
| "AUDIO_SOURCE_VOICE_UPLINK", "AUDIO_SOURCE_VOICE_DOWNLINK", |
| "AUDIO_SOURCE_VOICE_CALL", "AUDIO_SOURCE_CAMCORDER", |
| "AUDIO_SOURCE_VOICE_RECOGNITION", "AUDIO_SOURCE_VOICE_COMMUNICATION", |
| "AUDIO_SOURCE_REMOTE_SUBMIX", "AUDIO_SOURCE_UNPROCESSED", |
| "AUDIO_SOURCE_VOICE_PERFORMANCE"}; |
| |
| if (static_cast<uint32_t>(source) < (sizeof(strArr) / sizeof(strArr[0]))) |
| return strArr[source]; |
| return "UNKNOWN"; |
| } |
| |
| AudioContexts GetAudioContextsFromSourceMetadata( |
| const std::vector<struct playback_track_metadata>& source_metadata) { |
| AudioContexts track_contexts; |
| for (auto& track : source_metadata) { |
| if (track.content_type == 0 && track.usage == 0) continue; |
| |
| LOG_INFO("%s: usage=%s(%d), content_type=%s(%d), gain=%f", __func__, |
| usageToString(track.usage).c_str(), track.usage, |
| contentTypeToString(track.content_type).c_str(), |
| track.content_type, track.gain); |
| |
| track_contexts.set( |
| AudioContentToLeAudioContext(track.content_type, track.usage)); |
| } |
| return track_contexts; |
| } |
| |
| AudioContexts GetAudioContextsFromSinkMetadata( |
| const std::vector<struct record_track_metadata>& sink_metadata) { |
| AudioContexts all_track_contexts; |
| |
| for (auto& track : sink_metadata) { |
| if (track.source == AUDIO_SOURCE_INVALID) continue; |
| LeAudioContextType track_context; |
| |
| LOG_DEBUG( |
| "source=%s(0x%02x), gain=%f, destination device=0x%08x, destination " |
| "device address=%.32s", |
| audioSourceToStr(track.source), track.source, track.gain, |
| track.dest_device, track.dest_device_address); |
| |
| if (track.source == AUDIO_SOURCE_MIC) { |
| track_context = LeAudioContextType::LIVE; |
| |
| } else if (track.source == AUDIO_SOURCE_VOICE_COMMUNICATION) { |
| track_context = LeAudioContextType::CONVERSATIONAL; |
| |
| } else { |
| /* Fallback to voice assistant |
| * This will handle also a case when the device is |
| * AUDIO_SOURCE_VOICE_RECOGNITION |
| */ |
| track_context = LeAudioContextType::VOICEASSISTANTS; |
| LOG_WARN( |
| "Could not match the recording track type to group available " |
| "context. Using context %s.", |
| ToString(track_context).c_str()); |
| } |
| |
| all_track_contexts.set(track_context); |
| } |
| |
| if (all_track_contexts.none()) { |
| all_track_contexts = AudioContexts( |
| static_cast<std::underlying_type<LeAudioContextType>::type>( |
| LeAudioContextType::UNSPECIFIED)); |
| LOG_DEBUG( |
| "Unable to find supported audio source context for the remote audio " |
| "sink device. This may result in voice back channel malfunction."); |
| } |
| |
| LOG_DEBUG("Allowed contexts from sink metadata: %s (0x%08hx)", |
| bluetooth::common::ToString(all_track_contexts).c_str(), |
| all_track_contexts.value()); |
| return all_track_contexts; |
| } |
| |
| } // namespace utils |
| } // namespace le_audio |