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