webrtc_m130/sdk/android/api/org/webrtc/audio/JavaAudioDeviceModule.java
Paulina Hensman 1ca30a7e41 Decouple input and output sample rate overrides.
We may sometimes want to override only input or only output, or
override them with different values. This change allows setting the
overrides separately.


Change-Id: Ib0c44cb7a3cfa834f997fb6cd54f7cad68705f41
Bug: webrtc:10441
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/128763
Commit-Queue: Paulina Hensman <phensman@webrtc.org>
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27236}
2019-03-22 11:56:01 +00:00

346 lines
12 KiB
Java

/*
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc.audio;
import android.media.AudioManager;
import android.content.Context;
import org.webrtc.JniCommon;
import org.webrtc.Logging;
/**
* AudioDeviceModule implemented using android.media.AudioRecord as input and
* android.media.AudioTrack as output.
*/
public class JavaAudioDeviceModule implements AudioDeviceModule {
private static final String TAG = "JavaAudioDeviceModule";
public static Builder builder(Context context) {
return new Builder(context);
}
public static class Builder {
private final Context context;
private final AudioManager audioManager;
private int inputSampleRate;
private int outputSampleRate;
private int audioSource = WebRtcAudioRecord.DEFAULT_AUDIO_SOURCE;
private int audioFormat = WebRtcAudioRecord.DEFAULT_AUDIO_FORMAT;
private AudioTrackErrorCallback audioTrackErrorCallback;
private AudioRecordErrorCallback audioRecordErrorCallback;
private SamplesReadyCallback samplesReadyCallback;
private boolean useHardwareAcousticEchoCanceler = isBuiltInAcousticEchoCancelerSupported();
private boolean useHardwareNoiseSuppressor = isBuiltInNoiseSuppressorSupported();
private boolean useStereoInput;
private boolean useStereoOutput;
private Builder(Context context) {
this.context = context;
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
this.inputSampleRate = WebRtcAudioManager.getSampleRate(audioManager);
this.outputSampleRate = WebRtcAudioManager.getSampleRate(audioManager);
}
/**
* Call this method if the default handling of querying the native sample rate shall be
* overridden. Can be useful on some devices where the available Android APIs are known to
* return invalid results.
*/
public Builder setSampleRate(int sampleRate) {
Logging.d(TAG, "Input/Output sample rate overridden to: " + sampleRate);
this.inputSampleRate = sampleRate;
this.outputSampleRate = sampleRate;
return this;
}
/**
* Call this method to specifically override input sample rate.
*/
public Builder setInputSampleRate(int inputSampleRate) {
Logging.d(TAG, "Input sample rate overridden to: " + inputSampleRate);
this.inputSampleRate = inputSampleRate;
return this;
}
/**
* Call this method to specifically override output sample rate.
*/
public Builder setOutputSampleRate(int outputSampleRate) {
Logging.d(TAG, "Output sample rate overridden to: " + outputSampleRate);
this.outputSampleRate = outputSampleRate;
return this;
}
/**
* Call this to change the audio source. The argument should be one of the values from
* android.media.MediaRecorder.AudioSource. The default is AudioSource.VOICE_COMMUNICATION.
*/
public Builder setAudioSource(int audioSource) {
this.audioSource = audioSource;
return this;
}
/**
* Call this to change the audio format. The argument should be one of the values from
* android.media.AudioFormat ENCODING_PCM_8BIT, ENCODING_PCM_16BIT or ENCODING_PCM_FLOAT.
* Default audio data format is PCM 16 bit per sample.
* Guaranteed to be supported by all devices.
*/
public Builder setAudioFormat(int audioFormat) {
this.audioFormat = audioFormat;
return this;
}
/**
* Set a callback to retrieve errors from the AudioTrack.
*/
public Builder setAudioTrackErrorCallback(AudioTrackErrorCallback audioTrackErrorCallback) {
this.audioTrackErrorCallback = audioTrackErrorCallback;
return this;
}
/**
* Set a callback to retrieve errors from the AudioRecord.
*/
public Builder setAudioRecordErrorCallback(AudioRecordErrorCallback audioRecordErrorCallback) {
this.audioRecordErrorCallback = audioRecordErrorCallback;
return this;
}
/**
* Set a callback to listen to the raw audio input from the AudioRecord.
*/
public Builder setSamplesReadyCallback(SamplesReadyCallback samplesReadyCallback) {
this.samplesReadyCallback = samplesReadyCallback;
return this;
}
/**
* Control if the built-in HW noise suppressor should be used or not. The default is on if it is
* supported. It is possible to query support by calling isBuiltInNoiseSuppressorSupported().
*/
public Builder setUseHardwareNoiseSuppressor(boolean useHardwareNoiseSuppressor) {
if (useHardwareNoiseSuppressor && !isBuiltInNoiseSuppressorSupported()) {
Logging.e(TAG, "HW NS not supported");
useHardwareNoiseSuppressor = false;
}
this.useHardwareNoiseSuppressor = useHardwareNoiseSuppressor;
return this;
}
/**
* Control if the built-in HW acoustic echo canceler should be used or not. The default is on if
* it is supported. It is possible to query support by calling
* isBuiltInAcousticEchoCancelerSupported().
*/
public Builder setUseHardwareAcousticEchoCanceler(boolean useHardwareAcousticEchoCanceler) {
if (useHardwareAcousticEchoCanceler && !isBuiltInAcousticEchoCancelerSupported()) {
Logging.e(TAG, "HW AEC not supported");
useHardwareAcousticEchoCanceler = false;
}
this.useHardwareAcousticEchoCanceler = useHardwareAcousticEchoCanceler;
return this;
}
/**
* Control if stereo input should be used or not. The default is mono.
*/
public Builder setUseStereoInput(boolean useStereoInput) {
this.useStereoInput = useStereoInput;
return this;
}
/**
* Control if stereo output should be used or not. The default is mono.
*/
public Builder setUseStereoOutput(boolean useStereoOutput) {
this.useStereoOutput = useStereoOutput;
return this;
}
/**
* Construct an AudioDeviceModule based on the supplied arguments. The caller takes ownership
* and is responsible for calling release().
*/
public AudioDeviceModule createAudioDeviceModule() {
Logging.d(TAG, "createAudioDeviceModule");
if (useHardwareNoiseSuppressor) {
Logging.d(TAG, "HW NS will be used.");
} else {
if (isBuiltInNoiseSuppressorSupported()) {
Logging.d(TAG, "Overriding default behavior; now using WebRTC NS!");
}
Logging.d(TAG, "HW NS will not be used.");
}
if (useHardwareAcousticEchoCanceler) {
Logging.d(TAG, "HW AEC will be used.");
} else {
if (isBuiltInAcousticEchoCancelerSupported()) {
Logging.d(TAG, "Overriding default behavior; now using WebRTC AEC!");
}
Logging.d(TAG, "HW AEC will not be used.");
}
final WebRtcAudioRecord audioInput = new WebRtcAudioRecord(context, audioManager, audioSource,
audioFormat, audioRecordErrorCallback, samplesReadyCallback,
useHardwareAcousticEchoCanceler, useHardwareNoiseSuppressor);
final WebRtcAudioTrack audioOutput =
new WebRtcAudioTrack(context, audioManager, audioTrackErrorCallback);
return new JavaAudioDeviceModule(context, audioManager, audioInput, audioOutput,
inputSampleRate, outputSampleRate, useStereoInput, useStereoOutput);
}
}
/* AudioRecord */
// Audio recording error handler functions.
public enum AudioRecordStartErrorCode {
AUDIO_RECORD_START_EXCEPTION,
AUDIO_RECORD_START_STATE_MISMATCH,
}
public static interface AudioRecordErrorCallback {
void onWebRtcAudioRecordInitError(String errorMessage);
void onWebRtcAudioRecordStartError(AudioRecordStartErrorCode errorCode, String errorMessage);
void onWebRtcAudioRecordError(String errorMessage);
}
/**
* Contains audio sample information.
*/
public static class AudioSamples {
/** See {@link AudioRecord#getAudioFormat()} */
private final int audioFormat;
/** See {@link AudioRecord#getChannelCount()} */
private final int channelCount;
/** See {@link AudioRecord#getSampleRate()} */
private final int sampleRate;
private final byte[] data;
public AudioSamples(int audioFormat, int channelCount, int sampleRate, byte[] data) {
this.audioFormat = audioFormat;
this.channelCount = channelCount;
this.sampleRate = sampleRate;
this.data = data;
}
public int getAudioFormat() {
return audioFormat;
}
public int getChannelCount() {
return channelCount;
}
public int getSampleRate() {
return sampleRate;
}
public byte[] getData() {
return data;
}
}
/** Called when new audio samples are ready. This should only be set for debug purposes */
public static interface SamplesReadyCallback {
void onWebRtcAudioRecordSamplesReady(AudioSamples samples);
}
/* AudioTrack */
// Audio playout/track error handler functions.
public enum AudioTrackStartErrorCode {
AUDIO_TRACK_START_EXCEPTION,
AUDIO_TRACK_START_STATE_MISMATCH,
}
public static interface AudioTrackErrorCallback {
void onWebRtcAudioTrackInitError(String errorMessage);
void onWebRtcAudioTrackStartError(AudioTrackStartErrorCode errorCode, String errorMessage);
void onWebRtcAudioTrackError(String errorMessage);
}
/**
* Returns true if the device supports built-in HW AEC, and the UUID is approved (some UUIDs can
* be excluded).
*/
public static boolean isBuiltInAcousticEchoCancelerSupported() {
return WebRtcAudioEffects.isAcousticEchoCancelerSupported();
}
/**
* Returns true if the device supports built-in HW NS, and the UUID is approved (some UUIDs can be
* excluded).
*/
public static boolean isBuiltInNoiseSuppressorSupported() {
return WebRtcAudioEffects.isNoiseSuppressorSupported();
}
private final Context context;
private final AudioManager audioManager;
private final WebRtcAudioRecord audioInput;
private final WebRtcAudioTrack audioOutput;
private final int inputSampleRate;
private final int outputSampleRate;
private final boolean useStereoInput;
private final boolean useStereoOutput;
private final Object nativeLock = new Object();
private long nativeAudioDeviceModule;
private JavaAudioDeviceModule(Context context, AudioManager audioManager,
WebRtcAudioRecord audioInput, WebRtcAudioTrack audioOutput, int inputSampleRate,
int outputSampleRate, boolean useStereoInput, boolean useStereoOutput) {
this.context = context;
this.audioManager = audioManager;
this.audioInput = audioInput;
this.audioOutput = audioOutput;
this.inputSampleRate = inputSampleRate;
this.outputSampleRate = outputSampleRate;
this.useStereoInput = useStereoInput;
this.useStereoOutput = useStereoOutput;
}
@Override
public long getNativeAudioDeviceModulePointer() {
synchronized (nativeLock) {
if (nativeAudioDeviceModule == 0) {
nativeAudioDeviceModule = nativeCreateAudioDeviceModule(context, audioManager, audioInput,
audioOutput, inputSampleRate, outputSampleRate, useStereoInput, useStereoOutput);
}
return nativeAudioDeviceModule;
}
}
@Override
public void release() {
synchronized (nativeLock) {
if (nativeAudioDeviceModule != 0) {
JniCommon.nativeReleaseRef(nativeAudioDeviceModule);
nativeAudioDeviceModule = 0;
}
}
}
@Override
public void setSpeakerMute(boolean mute) {
Logging.d(TAG, "setSpeakerMute: " + mute);
audioOutput.setSpeakerMute(mute);
}
@Override
public void setMicrophoneMute(boolean mute) {
Logging.d(TAG, "setMicrophoneMute: " + mute);
audioInput.setMicrophoneMute(mute);
}
private static native long nativeCreateAudioDeviceModule(Context context,
AudioManager audioManager, WebRtcAudioRecord audioInput, WebRtcAudioTrack audioOutput,
int inputSampleRate, int outputSampleRate, boolean useStereoInput, boolean useStereoOutput);
}