1 /* 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc.audio; 12 13 import android.content.Context; 14 import android.content.pm.PackageManager; 15 import android.media.AudioFormat; 16 import android.media.AudioManager; 17 import android.media.AudioRecord; 18 import android.media.AudioTrack; 19 import android.os.Build; 20 import org.webrtc.Logging; 21 import org.webrtc.CalledByNative; 22 23 /** 24 * This class contains static functions to query sample rate and input/output audio buffer sizes. 25 */ 26 class WebRtcAudioManager { 27 private static final String TAG = "WebRtcAudioManagerExternal"; 28 29 private static final int DEFAULT_SAMPLE_RATE_HZ = 16000; 30 31 // Default audio data format is PCM 16 bit per sample. 32 // Guaranteed to be supported by all devices. 33 private static final int BITS_PER_SAMPLE = 16; 34 35 private static final int DEFAULT_FRAME_PER_BUFFER = 256; 36 37 @CalledByNative getAudioManager(Context context)38 static AudioManager getAudioManager(Context context) { 39 return (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 40 } 41 42 @CalledByNative getOutputBufferSize( Context context, AudioManager audioManager, int sampleRate, int numberOfOutputChannels)43 static int getOutputBufferSize( 44 Context context, AudioManager audioManager, int sampleRate, int numberOfOutputChannels) { 45 return isLowLatencyOutputSupported(context) 46 ? getLowLatencyFramesPerBuffer(audioManager) 47 : getMinOutputFrameSize(sampleRate, numberOfOutputChannels); 48 } 49 50 @CalledByNative getInputBufferSize( Context context, AudioManager audioManager, int sampleRate, int numberOfInputChannels)51 static int getInputBufferSize( 52 Context context, AudioManager audioManager, int sampleRate, int numberOfInputChannels) { 53 return isLowLatencyInputSupported(context) 54 ? getLowLatencyFramesPerBuffer(audioManager) 55 : getMinInputFrameSize(sampleRate, numberOfInputChannels); 56 } 57 isLowLatencyOutputSupported(Context context)58 private static boolean isLowLatencyOutputSupported(Context context) { 59 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY); 60 } 61 isLowLatencyInputSupported(Context context)62 private static boolean isLowLatencyInputSupported(Context context) { 63 // TODO(henrika): investigate if some sort of device list is needed here 64 // as well. The NDK doc states that: "As of API level 21, lower latency 65 // audio input is supported on select devices. To take advantage of this 66 // feature, first confirm that lower latency output is available". 67 return Build.VERSION.SDK_INT >= 21 && isLowLatencyOutputSupported(context); 68 } 69 70 /** 71 * Returns the native input/output sample rate for this device's output stream. 72 */ 73 @CalledByNative getSampleRate(AudioManager audioManager)74 static int getSampleRate(AudioManager audioManager) { 75 // Override this if we're running on an old emulator image which only 76 // supports 8 kHz and doesn't support PROPERTY_OUTPUT_SAMPLE_RATE. 77 if (WebRtcAudioUtils.runningOnEmulator()) { 78 Logging.d(TAG, "Running emulator, overriding sample rate to 8 kHz."); 79 return 8000; 80 } 81 // Deliver best possible estimate based on default Android AudioManager APIs. 82 final int sampleRateHz = getSampleRateForApiLevel(audioManager); 83 Logging.d(TAG, "Sample rate is set to " + sampleRateHz + " Hz"); 84 return sampleRateHz; 85 } 86 getSampleRateForApiLevel(AudioManager audioManager)87 private static int getSampleRateForApiLevel(AudioManager audioManager) { 88 if (Build.VERSION.SDK_INT < 17) { 89 return DEFAULT_SAMPLE_RATE_HZ; 90 } 91 String sampleRateString = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); 92 return (sampleRateString == null) ? DEFAULT_SAMPLE_RATE_HZ : Integer.parseInt(sampleRateString); 93 } 94 95 // Returns the native output buffer size for low-latency output streams. getLowLatencyFramesPerBuffer(AudioManager audioManager)96 private static int getLowLatencyFramesPerBuffer(AudioManager audioManager) { 97 if (Build.VERSION.SDK_INT < 17) { 98 return DEFAULT_FRAME_PER_BUFFER; 99 } 100 String framesPerBuffer = 101 audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); 102 return framesPerBuffer == null ? DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer); 103 } 104 105 // Returns the minimum output buffer size for Java based audio (AudioTrack). 106 // This size can also be used for OpenSL ES implementations on devices that 107 // lacks support of low-latency output. getMinOutputFrameSize(int sampleRateInHz, int numChannels)108 private static int getMinOutputFrameSize(int sampleRateInHz, int numChannels) { 109 final int bytesPerFrame = numChannels * (BITS_PER_SAMPLE / 8); 110 final int channelConfig = 111 (numChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO); 112 return AudioTrack.getMinBufferSize( 113 sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT) 114 / bytesPerFrame; 115 } 116 117 // Returns the minimum input buffer size for Java based audio (AudioRecord). 118 // This size can calso be used for OpenSL ES implementations on devices that 119 // lacks support of low-latency input. getMinInputFrameSize(int sampleRateInHz, int numChannels)120 private static int getMinInputFrameSize(int sampleRateInHz, int numChannels) { 121 final int bytesPerFrame = numChannels * (BITS_PER_SAMPLE / 8); 122 final int channelConfig = 123 (numChannels == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO); 124 return AudioRecord.getMinBufferSize( 125 sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT) 126 / bytesPerFrame; 127 } 128 } 129