| /* | |
| * Copyright 2016 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 <stdlib.h> | |
| #include <unistd.h> | |
| #include <set> | |
| #include <sstream> | |
| #ifdef __ANDROID__ | |
| #include <sys/system_properties.h> | |
| #endif | |
| #include <oboe/AudioStream.h> | |
| #include "oboe/Definitions.h" | |
| #include "oboe/Utilities.h" | |
| namespace oboe { | |
| constexpr float kScaleI16ToFloat = (1.0f / 32768.0f); | |
| void convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples) { | |
| for (int i = 0; i < numSamples; i++) { | |
| float fval = source[i]; | |
| fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation | |
| fval *= 32768.0f; | |
| auto sample = static_cast<int32_t>(fval); | |
| // clip to 16-bit range | |
| if (sample < 0) sample = 0; | |
| else if (sample > 0x0FFFF) sample = 0x0FFFF; | |
| sample -= 32768; // center at zero | |
| destination[i] = static_cast<int16_t>(sample); | |
| } | |
| } | |
| void convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples) { | |
| for (int i = 0; i < numSamples; i++) { | |
| destination[i] = source[i] * kScaleI16ToFloat; | |
| } | |
| } | |
| int32_t convertFormatToSizeInBytes(AudioFormat format) { | |
| int32_t size = 0; | |
| switch (format) { | |
| case AudioFormat::I16: | |
| size = sizeof(int16_t); | |
| break; | |
| case AudioFormat::Float: | |
| size = sizeof(float); | |
| break; | |
| case AudioFormat::I24: | |
| size = 3; // packed 24-bit data | |
| break; | |
| case AudioFormat::I32: | |
| size = sizeof(int32_t); | |
| break; | |
| case AudioFormat::IEC61937: | |
| size = sizeof(int16_t); | |
| break; | |
| case AudioFormat::MP3: | |
| case AudioFormat::AAC_LC: | |
| case AudioFormat::AAC_HE_V1: | |
| case AudioFormat::AAC_HE_V2: | |
| case AudioFormat::AAC_ELD: | |
| case AudioFormat::AAC_XHE: | |
| case AudioFormat::OPUS: | |
| // For compressed formats, set the size per sample as 0 as they may not have | |
| // fix size per sample. | |
| size = 0; | |
| break; | |
| default: | |
| break; | |
| } | |
| return size; | |
| } | |
| template<> | |
| const char *convertToText<Result>(Result returnCode) { | |
| switch (returnCode) { | |
| case Result::OK: return "OK"; | |
| case Result::ErrorDisconnected: return "ErrorDisconnected"; | |
| case Result::ErrorIllegalArgument: return "ErrorIllegalArgument"; | |
| case Result::ErrorInternal: return "ErrorInternal"; | |
| case Result::ErrorInvalidState: return "ErrorInvalidState"; | |
| case Result::ErrorInvalidHandle: return "ErrorInvalidHandle"; | |
| case Result::ErrorUnimplemented: return "ErrorUnimplemented"; | |
| case Result::ErrorUnavailable: return "ErrorUnavailable"; | |
| case Result::ErrorNoFreeHandles: return "ErrorNoFreeHandles"; | |
| case Result::ErrorNoMemory: return "ErrorNoMemory"; | |
| case Result::ErrorNull: return "ErrorNull"; | |
| case Result::ErrorTimeout: return "ErrorTimeout"; | |
| case Result::ErrorWouldBlock: return "ErrorWouldBlock"; | |
| case Result::ErrorInvalidFormat: return "ErrorInvalidFormat"; | |
| case Result::ErrorOutOfRange: return "ErrorOutOfRange"; | |
| case Result::ErrorNoService: return "ErrorNoService"; | |
| case Result::ErrorInvalidRate: return "ErrorInvalidRate"; | |
| case Result::ErrorClosed: return "ErrorClosed"; | |
| default: return "Unrecognized result"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<AudioFormat>(AudioFormat format) { | |
| switch (format) { | |
| case AudioFormat::Invalid: return "Invalid"; | |
| case AudioFormat::Unspecified: return "Unspecified"; | |
| case AudioFormat::I16: return "I16"; | |
| case AudioFormat::Float: return "Float"; | |
| case AudioFormat::I24: return "I24"; | |
| case AudioFormat::I32: return "I32"; | |
| case AudioFormat::IEC61937: return "IEC61937"; | |
| case AudioFormat::MP3: return "MP3"; | |
| case AudioFormat::AAC_LC: return "AAC_LC"; | |
| case AudioFormat::AAC_HE_V1: return "AAC_HE_V1"; | |
| case AudioFormat::AAC_HE_V2: return "AAC_HE_V2"; | |
| case AudioFormat::AAC_ELD: return "AAC_ELD"; | |
| case AudioFormat::AAC_XHE: return "AAC_XHE"; | |
| case AudioFormat::OPUS: return "OPUS"; | |
| default: return "Unrecognized format"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<PerformanceMode>(PerformanceMode mode) { | |
| switch (mode) { | |
| case PerformanceMode::LowLatency: return "LowLatency"; | |
| case PerformanceMode::None: return "None"; | |
| case PerformanceMode::PowerSaving: return "PowerSaving"; | |
| default: return "Unrecognized performance mode"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<SharingMode>(SharingMode mode) { | |
| switch (mode) { | |
| case SharingMode::Exclusive: return "Exclusive"; | |
| case SharingMode::Shared: return "Shared"; | |
| default: return "Unrecognized sharing mode"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<DataCallbackResult>(DataCallbackResult result) { | |
| switch (result) { | |
| case DataCallbackResult::Continue: return "Continue"; | |
| case DataCallbackResult::Stop: return "Stop"; | |
| default: return "Unrecognized data callback result"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<Direction>(Direction direction) { | |
| switch (direction) { | |
| case Direction::Input: return "Input"; | |
| case Direction::Output: return "Output"; | |
| default: return "Unrecognized direction"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<StreamState>(StreamState state) { | |
| switch (state) { | |
| case StreamState::Closed: return "Closed"; | |
| case StreamState::Closing: return "Closing"; | |
| case StreamState::Disconnected: return "Disconnected"; | |
| case StreamState::Flushed: return "Flushed"; | |
| case StreamState::Flushing: return "Flushing"; | |
| case StreamState::Open: return "Open"; | |
| case StreamState::Paused: return "Paused"; | |
| case StreamState::Pausing: return "Pausing"; | |
| case StreamState::Started: return "Started"; | |
| case StreamState::Starting: return "Starting"; | |
| case StreamState::Stopped: return "Stopped"; | |
| case StreamState::Stopping: return "Stopping"; | |
| case StreamState::Uninitialized: return "Uninitialized"; | |
| case StreamState::Unknown: return "Unknown"; | |
| default: return "Unrecognized stream state"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<AudioApi>(AudioApi audioApi) { | |
| switch (audioApi) { | |
| case AudioApi::Unspecified: return "Unspecified"; | |
| case AudioApi::OpenSLES: return "OpenSLES"; | |
| case AudioApi::AAudio: return "AAudio"; | |
| default: return "Unrecognized audio API"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<AudioStream*>(AudioStream* stream) { | |
| static std::string streamText; | |
| std::stringstream s; | |
| s<<"StreamID: "<< static_cast<void*>(stream)<<std::endl | |
| <<"DeviceId: "<<stream->getDeviceId()<<std::endl | |
| <<"Direction: "<<oboe::convertToText(stream->getDirection())<<std::endl | |
| <<"API type: "<<oboe::convertToText(stream->getAudioApi())<<std::endl | |
| <<"BufferCapacity: "<<stream->getBufferCapacityInFrames()<<std::endl | |
| <<"BufferSize: "<<stream->getBufferSizeInFrames()<<std::endl | |
| <<"FramesPerBurst: "<< stream->getFramesPerBurst()<<std::endl | |
| <<"FramesPerDataCallback: "<<stream->getFramesPerDataCallback()<<std::endl | |
| <<"SampleRate: "<<stream->getSampleRate()<<std::endl | |
| <<"ChannelCount: "<<stream->getChannelCount()<<std::endl | |
| <<"Format: "<<oboe::convertToText(stream->getFormat())<<std::endl | |
| <<"SharingMode: "<<oboe::convertToText(stream->getSharingMode())<<std::endl | |
| <<"PerformanceMode: "<<oboe::convertToText(stream->getPerformanceMode()) | |
| <<std::endl | |
| <<"CurrentState: "<<oboe::convertToText(stream->getState())<<std::endl | |
| <<"XRunCount: "<<stream->getXRunCount()<<std::endl | |
| <<"FramesRead: "<<stream->getFramesRead()<<std::endl | |
| <<"FramesWritten: "<<stream->getFramesWritten()<<std::endl; | |
| streamText = s.str(); | |
| return streamText.c_str(); | |
| } | |
| template<> | |
| const char *convertToText<Usage>(Usage usage) { | |
| switch (usage) { | |
| case Usage::Media: return "Media"; | |
| case Usage::VoiceCommunication: return "VoiceCommunication"; | |
| case Usage::VoiceCommunicationSignalling: return "VoiceCommunicationSignalling"; | |
| case Usage::Alarm: return "Alarm"; | |
| case Usage::Notification: return "Notification"; | |
| case Usage::NotificationRingtone: return "NotificationRingtone"; | |
| case Usage::NotificationEvent: return "NotificationEvent"; | |
| case Usage::AssistanceAccessibility: return "AssistanceAccessibility"; | |
| case Usage::AssistanceNavigationGuidance: return "AssistanceNavigationGuidance"; | |
| case Usage::AssistanceSonification: return "AssistanceSonification"; | |
| case Usage::Game: return "Game"; | |
| case Usage::Assistant: return "Assistant"; | |
| default: return "Unrecognized usage"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<ContentType>(ContentType contentType) { | |
| switch (contentType) { | |
| case ContentType::Speech: return "Speech"; | |
| case ContentType::Music: return "Music"; | |
| case ContentType::Movie: return "Movie"; | |
| case ContentType::Sonification: return "Sonification"; | |
| default: return "Unrecognized content type"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<InputPreset>(InputPreset inputPreset) { | |
| switch (inputPreset) { | |
| case InputPreset::Generic: return "Generic"; | |
| case InputPreset::Camcorder: return "Camcorder"; | |
| case InputPreset::VoiceRecognition: return "VoiceRecognition"; | |
| case InputPreset::VoiceCommunication: return "VoiceCommunication"; | |
| case InputPreset::Unprocessed: return "Unprocessed"; | |
| case InputPreset::VoicePerformance: return "VoicePerformance"; | |
| default: return "Unrecognized input preset"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<SessionId>(SessionId sessionId) { | |
| switch (sessionId) { | |
| case SessionId::None: return "None"; | |
| case SessionId::Allocate: return "Allocate"; | |
| default: return "Unrecognized session id"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<ChannelCount>(ChannelCount channelCount) { | |
| switch (channelCount) { | |
| case ChannelCount::Unspecified: return "Unspecified"; | |
| case ChannelCount::Mono: return "Mono"; | |
| case ChannelCount::Stereo: return "Stereo"; | |
| default: return "Unrecognized channel count"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<SampleRateConversionQuality>(SampleRateConversionQuality sampleRateConversionQuality) { | |
| switch (sampleRateConversionQuality) { | |
| case SampleRateConversionQuality::None: return "None"; | |
| case SampleRateConversionQuality::Fastest: return "Fastest"; | |
| case SampleRateConversionQuality::Low: return "Low"; | |
| case SampleRateConversionQuality::Medium: return "Medium"; | |
| case SampleRateConversionQuality::High: return "High"; | |
| case SampleRateConversionQuality::Best: return "Best"; | |
| default: return "Unrecognized sample rate conversion quality"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<FallbackMode>(FallbackMode fallbackMode) { | |
| switch (fallbackMode) { | |
| case FallbackMode::Default: return "Default"; | |
| case FallbackMode::Fail: return "Fail"; | |
| case FallbackMode::Mute: return "Mute"; | |
| default: return "Unrecognized fallback mode"; | |
| } | |
| } | |
| template<> | |
| const char *convertToText<StretchMode>(StretchMode stretchMode) { | |
| switch (stretchMode) { | |
| case StretchMode::Default: return "Default"; | |
| case StretchMode::Voice: return "Voice"; | |
| default: return "Unrecognized stretch mode"; | |
| } | |
| } | |
| std::string getPropertyString(const char * name) { | |
| std::string result; | |
| #ifdef __ANDROID__ | |
| char valueText[PROP_VALUE_MAX] = {0}; | |
| if (__system_property_get(name, valueText) != 0) { | |
| result = valueText; | |
| } | |
| #else | |
| (void) name; | |
| #endif | |
| return result; | |
| } | |
| int getPropertyInteger(const char * name, int defaultValue) { | |
| int result = defaultValue; | |
| #ifdef __ANDROID__ | |
| char valueText[PROP_VALUE_MAX] = {0}; | |
| if (__system_property_get(name, valueText) != 0) { | |
| result = atoi(valueText); | |
| } | |
| #else | |
| (void) name; | |
| #endif | |
| return result; | |
| } | |
| int getSdkVersion() { | |
| static int sCachedSdkVersion = -1; | |
| #ifdef __ANDROID__ | |
| if (sCachedSdkVersion == -1) { | |
| sCachedSdkVersion = getPropertyInteger("ro.build.version.sdk", -1); | |
| } | |
| #endif | |
| return sCachedSdkVersion; | |
| } | |
| bool isAtLeastPreReleaseCodename(const std::string& codename) { | |
| std::string buildCodename = getPropertyString("ro.build.version.codename"); | |
| // Special case "REL", which means the build is not a pre-release build. | |
| if ("REL" == buildCodename) { | |
| return false; | |
| } | |
| // Otherwise lexically compare them. Return true if the build codename is equal to or | |
| // greater than the requested codename. | |
| return buildCodename.compare(codename) >= 0; | |
| } | |
| int getChannelCountFromChannelMask(ChannelMask channelMask) { | |
| return __builtin_popcount(static_cast<uint32_t>(channelMask)); | |
| } | |
| std::set<AudioFormat> COMPRESSED_FORMATS = { | |
| AudioFormat::MP3, AudioFormat::AAC_LC, AudioFormat::AAC_HE_V1, AudioFormat::AAC_HE_V2, | |
| AudioFormat::AAC_ELD, AudioFormat::AAC_XHE, AudioFormat::OPUS | |
| }; | |
| bool isCompressedFormat(AudioFormat format) { | |
| return COMPRESSED_FORMATS.count(format) != 0; | |
| } | |
| std::string toString(const PlaybackParameters& parameters) { | |
| std::stringstream ss; | |
| ss << "Fallback Mode: " << convertToText(parameters.fallbackMode) | |
| << ", Stretch Mode:" << convertToText(parameters.stretchMode) | |
| << ", pitch: " << parameters.pitch | |
| << ", speed: " << parameters.speed; | |
| return ss.str(); | |
| } | |
| }// namespace oboe |