script-astra/Android/Sdk/sources/android-35/android/companion/virtual/audio/AudioCapture.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

209 lines
7.1 KiB
Java

/*
* Copyright (C) 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.
*/
package android.companion.virtual.audio;
import static android.media.AudioRecord.READ_BLOCKING;
import static android.media.AudioRecord.RECORDSTATE_RECORDING;
import static android.media.AudioRecord.RECORDSTATE_STOPPED;
import static android.media.AudioRecord.STATE_INITIALIZED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.nio.ByteBuffer;
/**
* Wrapper around {@link AudioRecord} that allows for the underlying {@link AudioRecord} to
* be swapped out while recording is ongoing.
*
* @hide
*/
// The stop() actually doesn't release resources, so should not force implementing Closeable.
@SuppressLint("NotCloseable")
@SystemApi
public final class AudioCapture {
private static final String TAG = "AudioCapture";
private final AudioFormat mAudioFormat;
private final Object mLock = new Object();
@GuardedBy("mLock")
@Nullable
private AudioRecord mAudioRecord;
@GuardedBy("mLock")
private int mRecordingState = RECORDSTATE_STOPPED;
/**
* Sets the {@link AudioRecord} to handle audio capturing.
*
* <p>Callers may call this multiple times with different audio records to change the underlying
* {@link AudioRecord} without stopping and re-starting recording.
*
* @param audioRecord The underlying {@link AudioRecord} to use for capture, or null if no audio
* (i.e. silence) should be captured while still keeping the record in a recording state.
*/
void setAudioRecord(@Nullable AudioRecord audioRecord) {
Log.d(TAG, "set AudioRecord with " + audioRecord);
synchronized (mLock) {
// Sync recording state for new reference.
if (audioRecord != null) {
if (audioRecord.getState() != STATE_INITIALIZED) {
throw new IllegalStateException("set an uninitialized AudioRecord.");
}
if (mRecordingState == RECORDSTATE_RECORDING
&& audioRecord.getRecordingState() != RECORDSTATE_RECORDING) {
audioRecord.startRecording();
}
if (mRecordingState == RECORDSTATE_STOPPED
&& audioRecord.getRecordingState() != RECORDSTATE_STOPPED) {
audioRecord.stop();
}
}
// Release old reference before assigning the new reference.
if (mAudioRecord != null) {
mAudioRecord.release();
}
mAudioRecord = audioRecord;
}
}
AudioCapture(@NonNull AudioFormat audioFormat) {
mAudioFormat = audioFormat;
}
void close() {
synchronized (mLock) {
if (mAudioRecord != null) {
mAudioRecord.release();
mAudioRecord = null;
}
}
}
/** See {@link AudioRecord#getFormat()} */
public @NonNull AudioFormat getFormat() {
return mAudioFormat;
}
/** See {@link AudioRecord#read(byte[], int, int)} */
public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
return read(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING);
}
/** See {@link AudioRecord#read(byte[], int, int, int)} */
public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
@AudioRecord.ReadMode int readMode) {
final int sizeRead;
synchronized (mLock) {
if (mAudioRecord != null) {
sizeRead = mAudioRecord.read(audioData, offsetInBytes, sizeInBytes, readMode);
} else {
sizeRead = 0;
}
}
return sizeRead;
}
/** See {@link AudioRecord#read(ByteBuffer, int)}. */
public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) {
return read(audioBuffer, sizeInBytes, READ_BLOCKING);
}
/** See {@link AudioRecord#read(ByteBuffer, int, int)}. */
public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes,
@AudioRecord.ReadMode int readMode) {
final int sizeRead;
synchronized (mLock) {
if (mAudioRecord != null) {
sizeRead = mAudioRecord.read(audioBuffer, sizeInBytes, readMode);
} else {
sizeRead = 0;
}
}
return sizeRead;
}
/** See {@link AudioRecord#read(float[], int, int, int)}. */
public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
@AudioRecord.ReadMode int readMode) {
final int sizeRead;
synchronized (mLock) {
if (mAudioRecord != null) {
sizeRead = mAudioRecord.read(audioData, offsetInFloats, sizeInFloats, readMode);
} else {
sizeRead = 0;
}
}
return sizeRead;
}
/** See {@link AudioRecord#read(short[], int, int)}. */
public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING);
}
/** See {@link AudioRecord#read(short[], int, int, int)}. */
public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
@AudioRecord.ReadMode int readMode) {
final int sizeRead;
synchronized (mLock) {
if (mAudioRecord != null) {
sizeRead = mAudioRecord.read(audioData, offsetInShorts, sizeInShorts, readMode);
} else {
sizeRead = 0;
}
}
return sizeRead;
}
/** See {@link AudioRecord#startRecording()}. */
public void startRecording() {
synchronized (mLock) {
mRecordingState = RECORDSTATE_RECORDING;
if (mAudioRecord != null && mAudioRecord.getRecordingState() != RECORDSTATE_RECORDING) {
mAudioRecord.startRecording();
}
}
}
/** See {@link AudioRecord#stop()}. */
public void stop() {
synchronized (mLock) {
mRecordingState = RECORDSTATE_STOPPED;
if (mAudioRecord != null && mAudioRecord.getRecordingState() != RECORDSTATE_STOPPED) {
mAudioRecord.stop();
}
}
}
/** See {@link AudioRecord#getRecordingState()}. */
public int getRecordingState() {
synchronized (mLock) {
return mRecordingState;
}
}
}