AudioSettings.java

/*
 * Copyright 2023 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 androidx.camera.video.internal.audio;

import android.annotation.SuppressLint;
import android.media.AudioFormat;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.google.auto.value.AutoValue;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Settings required to configure the audio source.
 */
@SuppressWarnings("NullableProblems") // Problem from AutoValue generated class.
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
@AutoValue
public abstract class AudioSettings {

    // Common sample rate options to choose from in descending order.
    public static final List<Integer> COMMON_SAMPLE_RATES = Collections.unmodifiableList(
            Arrays.asList(48000, 44100, 22050, 11025, 8000, 4800));

    /** Creates a builder for these settings. */
    @SuppressLint("Range") // Need to initialize as invalid values
    @NonNull
    public static Builder builder() {
        return new AutoValue_AudioSettings.Builder()
                .setAudioSource(-1)
                .setSampleRate(-1)
                .setChannelCount(-1)
                .setAudioFormat(-1);
    }

    /** Creates a {@link Builder} initialized with the same settings as this instance. */
    @NonNull
    public abstract Builder toBuilder();

    /**
     * Gets the device audio source.
     *
     * @see android.media.MediaRecorder.AudioSource#MIC
     * @see android.media.MediaRecorder.AudioSource#CAMCORDER
     */
    public abstract int getAudioSource();

    /**
     * Gets the audio sample rate.
     */
    @IntRange(from = 1)
    public abstract int getSampleRate();

    /**
     * Gets the channel count.
     */
    @IntRange(from = 1)
    public abstract int getChannelCount();

    /**
     * Sets the audio format.
     *
     * @see AudioFormat#ENCODING_PCM_16BIT
     */
    public abstract int getAudioFormat();

    // Should not be instantiated directly
    AudioSettings() {
    }

    /** Gets the size in bytes per frame. */
    public int getBytesPerFrame() {
        return AudioUtils.getBytesPerFrame(getAudioFormat(), getChannelCount());
    }

    /**
     * A Builder for {@link AudioSettings}.
     */
    @AutoValue.Builder
    public abstract static class Builder {
        /**
         * Sets the device audio source.
         *
         * @see android.media.MediaRecorder.AudioSource#MIC
         * @see android.media.MediaRecorder.AudioSource#CAMCORDER
         */
        @NonNull
        public abstract Builder setAudioSource(int audioSource);

        /**
         * Sets the audio sample rate in Hertz.
         */
        @NonNull
        public abstract Builder setSampleRate(@IntRange(from = 1) int sampleRate);

        /**
         * Sets the channel count.
         */
        @NonNull
        public abstract Builder setChannelCount(@IntRange(from = 1) int channelCount);

        /**
         * Sets the audio format.
         *
         * @see AudioFormat#ENCODING_PCM_16BIT
         */
        @NonNull
        public abstract Builder setAudioFormat(int audioFormat);

        abstract AudioSettings autoBuild(); // Actual build method. Not public.

        /**
         * Returns the built config after performing settings validation.
         *
         * @throws IllegalArgumentException if a setting is missing or invalid.
         */
        @NonNull
        public final AudioSettings build() {
            AudioSettings settings = autoBuild();
            String missingOrInvalid = "";
            if (settings.getAudioSource() == -1) {
                missingOrInvalid += " audioSource";
            }
            if (settings.getSampleRate() <= 0) {
                missingOrInvalid += " sampleRate";
            }
            if (settings.getChannelCount() <= 0) {
                missingOrInvalid += " channelCount";
            }
            if (settings.getAudioFormat() == -1) {
                missingOrInvalid += " audioFormat";
            }

            if (!missingOrInvalid.isEmpty()) {
                throw new IllegalArgumentException("Required settings missing or "
                        + "non-positive:" + missingOrInvalid);
            }

            return settings;
        }

        // Should not be instantiated directly
        Builder() {
        }
    }
}