AudioMixer.java
/*
* Copyright 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 androidx.media3.transformer;
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
import androidx.media3.common.audio.AudioProcessor.UnhandledAudioFormatException;
import androidx.media3.common.util.UnstableApi;
import java.nio.ByteBuffer;
/**
* An audio component which combines audio data from multiple sources into a single output.
*
* <p>The mixer supports an arbitrary number of concurrent sources and will ensure audio data from
* all sources are aligned and mixed before producing output. Any periods without sources will be
* filled with silence. The total duration of the mixed track is controlled with {@link
* #setEndTimeUs}, or is unbounded if left unset.
*
* <p><b>Updates:</b> The mixer supports the following updates at any time without the need for a
* {@link #reset()}.
*
* <ul>
* <li>{@linkplain #addSource Add source}. Source audio will be included in future mixed output
* only.
* <li>{@linkplain #removeSource Remove source}.
* <li>{@linkplain #setSourceVolume Change source volume}. The new volume will apply only to
* future source samples.
* <li>{@linkplain #setEndTimeUs Change end time}. The new end time may cause an immediate change
* to the mixer {@linkplain #isEnded() ended state}.
* </ul>
*
* <p>{@linkplain #configure Changes} to the output audio format, buffer size, or mixer start time
* require the mixer to first be {@linkplain #reset() reset}, discarding all buffered data.
*
* <p><b>Operation:</b> The mixer must be {@linkplain #configure configured} before any methods are
* called. Once configured, sources can queue audio data via {@link #queueInput} and the mixer will
* consume input audio up to the configured buffer size and end time. Once all sources have produced
* data for a period then {@link #getOutput()} will return the mixed result. The cycle repeats until
* the mixer {@link #isEnded()}.
*/
@UnstableApi
public interface AudioMixer {
/** Creates an unconfigured instance. */
public static AudioMixer create() {
return new AudioMixerImpl();
}
/**
* Configures the mixer.
*
* <p>The mixer must be configured before use and can only be reconfigured after a call to {@link
* #reset()}.
*
* <p>The mixing buffer size is set by {@code bufferSizeMs} and indicates how much audio can be
* queued before {@link #getOutput()} is called.
*
* @param outputAudioFormat The audio format of buffers returned from {@link #getOutput()}.
* @param bufferSizeMs The mixing buffer size in milliseconds.
* @param startTimeUs The start time of the mixer output in microseconds.
* @throws UnhandledAudioFormatException If the output audio format is not supported.
*/
void configure(AudioFormat outputAudioFormat, int bufferSizeMs, long startTimeUs)
throws UnhandledAudioFormatException;
/**
* Sets the end time of the output audio.
*
* <p>The mixer will not accept input nor produce output past this point.
*
* @param endTimeUs The end time in microseconds.
* @throws IllegalArgumentException If {@code endTimeUs} is before the configured start time.
*/
void setEndTimeUs(long endTimeUs);
/** Indicates whether the mixer supports mixing sources with the given audio format. */
boolean supportsSourceAudioFormat(AudioFormat sourceFormat);
/**
* Adds an audio source to mix starting at the given time.
*
* <p>If the mixer has already {@linkplain #getOutput() output} samples past the {@code
* startTimeUs}, audio from this source will be discarded up to the last output end timestamp.
*
* <p>If the source start time is earlier than the configured mixer start time then audio from
* this source will be discarded up to the mixer start time.
*
* <p>All audio sources start with a volume of 1.0 on all channels.
*
* @param sourceFormat Audio format of source buffers.
* @param startTimeUs Source start time in microseconds.
* @return Non-negative integer identifying the source ({@code sourceId}).
* @throws UnhandledAudioFormatException If the source format is not supported.
*/
int addSource(AudioFormat sourceFormat, long startTimeUs) throws UnhandledAudioFormatException;
/**
* Sets the volume applied to future samples queued from the given source.
*
* @param sourceId Source identifier from {@link #addSource}.
* @param volume Non-negative scalar applied to all source channels.
*/
void setSourceVolume(int sourceId, float volume);
/**
* Removes an audio source.
*
* <p>No more audio can be queued from this source. All audio queued before removal will be
* output.
*
* @param sourceId Source identifier from {@link #addSource}.
*/
void removeSource(int sourceId);
/**
* Queues audio data between the position and limit of the {@code sourceBuffer}.
*
* <p>After calling this method output may be available via {@link #getOutput()} if all sources
* have queued data.
*
* @param sourceId Source identifier from {@link #addSource}.
* @param sourceBuffer The source buffer to mix. It must be a direct byte buffer with native byte
* order. Its contents are treated as read-only. Its position will be advanced by the number
* of bytes consumed (which may be zero). The caller retains ownership of the provided buffer.
*/
void queueInput(int sourceId, ByteBuffer sourceBuffer);
/**
* Returns a buffer containing output audio data between its position and limit.
*
* <p>The buffer will be no larger than the configured buffer size and will include no more than
* the frames that have been queued from all sources, up to the {@linkplain #setEndTimeUs end
* time}. Silence will be generated for any periods with no sources.
*
* <p>The buffer will always be a direct byte buffer with native byte order. Calling this method
* invalidates any previously returned buffer. The buffer will be empty if no output is available.
*
* @return A buffer containing output data between its position and limit.
*/
ByteBuffer getOutput();
/**
* Returns whether the mixer can accept more {@linkplain #queueInput input} or produce more
* {@linkplain #getOutput() output}, based on the {@link #setEndTimeUs end time}.
*
* <p><b>Note:</b> If no end time is set this will always return {@code false}.
*/
boolean isEnded();
/** Resets the mixer to its unconfigured state, releasing any resources. */
void reset();
}