mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Composition audio effects
Implement composition-level audio effects in AudioGraph. PiperOrigin-RevId: 610689632
This commit is contained in:
parent
0480bc31a8
commit
477ace1be9
7 changed files with 133 additions and 31 deletions
|
|
@ -16,6 +16,7 @@
|
||||||
negative presentation timestamps before API 30.
|
negative presentation timestamps before API 30.
|
||||||
* Relax trim optimization H.264 level checks.
|
* Relax trim optimization H.264 level checks.
|
||||||
* Add support for changing between SDR and HDR input media in a sequence.
|
* Add support for changing between SDR and HDR input media in a sequence.
|
||||||
|
* Add support for composition-level audio effects.
|
||||||
* Track Selection:
|
* Track Selection:
|
||||||
* Extractors:
|
* Extractors:
|
||||||
* Audio:
|
* Audio:
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,11 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.audio.AudioProcessingPipeline;
|
||||||
|
import androidx.media3.common.audio.AudioProcessor;
|
||||||
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
|
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
|
||||||
import androidx.media3.common.audio.AudioProcessor.UnhandledAudioFormatException;
|
import androidx.media3.common.audio.AudioProcessor.UnhandledAudioFormatException;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
@ -31,17 +34,19 @@ import java.util.Objects;
|
||||||
/* package */ final class AudioGraph {
|
/* package */ final class AudioGraph {
|
||||||
private final AudioMixer mixer;
|
private final AudioMixer mixer;
|
||||||
private final SparseArray<AudioGraphInput> inputs;
|
private final SparseArray<AudioGraphInput> inputs;
|
||||||
|
private final AudioProcessingPipeline audioProcessingPipeline;
|
||||||
|
|
||||||
private AudioFormat outputAudioFormat;
|
private AudioFormat mixerAudioFormat;
|
||||||
private int finishedInputs;
|
private int finishedInputs;
|
||||||
private ByteBuffer currentOutput;
|
private ByteBuffer mixerOutput;
|
||||||
|
|
||||||
/** Creates an instance. */
|
/** Creates an instance. */
|
||||||
public AudioGraph(AudioMixer.Factory mixerFactory) {
|
public AudioGraph(AudioMixer.Factory mixerFactory, ImmutableList<AudioProcessor> effects) {
|
||||||
mixer = mixerFactory.create();
|
mixer = mixerFactory.create();
|
||||||
inputs = new SparseArray<>();
|
inputs = new SparseArray<>();
|
||||||
currentOutput = EMPTY_BUFFER;
|
audioProcessingPipeline = new AudioProcessingPipeline(effects);
|
||||||
outputAudioFormat = AudioFormat.NOT_SET;
|
mixerOutput = EMPTY_BUFFER;
|
||||||
|
mixerAudioFormat = AudioFormat.NOT_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether an {@link AudioFormat} is valid as an input format. */
|
/** Returns whether an {@link AudioFormat} is valid as an input format. */
|
||||||
|
|
@ -65,13 +70,15 @@ import java.util.Objects;
|
||||||
*
|
*
|
||||||
* <p>Should be called at most once, before {@link #registerInput registering input}.
|
* <p>Should be called at most once, before {@link #registerInput registering input}.
|
||||||
*
|
*
|
||||||
* @param requestedAudioFormat The {@link AudioFormat} requested for output from the mixer.
|
* @param mixerAudioFormat The {@link AudioFormat} requested for output from the mixer.
|
||||||
* @throws UnhandledAudioFormatException If the audio format is not supported by the {@link
|
* @throws UnhandledAudioFormatException If the audio format is not supported by the {@link
|
||||||
* AudioMixer}.
|
* AudioMixer}.
|
||||||
*/
|
*/
|
||||||
public void configure(AudioFormat requestedAudioFormat) throws UnhandledAudioFormatException {
|
public void configure(AudioFormat mixerAudioFormat) throws UnhandledAudioFormatException {
|
||||||
this.outputAudioFormat = requestedAudioFormat;
|
this.mixerAudioFormat = mixerAudioFormat;
|
||||||
mixer.configure(requestedAudioFormat, /* bufferSizeMs= */ C.LENGTH_UNSET, /* startTimeUs= */ 0);
|
mixer.configure(mixerAudioFormat, /* bufferSizeMs= */ C.LENGTH_UNSET, /* startTimeUs= */ 0);
|
||||||
|
audioProcessingPipeline.configure(mixerAudioFormat);
|
||||||
|
audioProcessingPipeline.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -85,9 +92,9 @@ import java.util.Objects;
|
||||||
checkArgument(format.pcmEncoding != Format.NO_VALUE);
|
checkArgument(format.pcmEncoding != Format.NO_VALUE);
|
||||||
try {
|
try {
|
||||||
AudioGraphInput audioGraphInput =
|
AudioGraphInput audioGraphInput =
|
||||||
new AudioGraphInput(outputAudioFormat, editedMediaItem, format);
|
new AudioGraphInput(mixerAudioFormat, editedMediaItem, format);
|
||||||
|
|
||||||
if (Objects.equals(outputAudioFormat, AudioFormat.NOT_SET)) {
|
if (Objects.equals(mixerAudioFormat, AudioFormat.NOT_SET)) {
|
||||||
// Graph not configured, configure before doing anything else.
|
// Graph not configured, configure before doing anything else.
|
||||||
configure(audioGraphInput.getOutputAudioFormat());
|
configure(audioGraphInput.getOutputAudioFormat());
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +112,7 @@ import java.util.Objects;
|
||||||
* AudioFormat#NOT_SET} if not {@linkplain #configure configured}.
|
* AudioFormat#NOT_SET} if not {@linkplain #configure configured}.
|
||||||
*/
|
*/
|
||||||
public AudioFormat getOutputAudioFormat() {
|
public AudioFormat getOutputAudioFormat() {
|
||||||
return outputAudioFormat;
|
return audioProcessingPipeline.getOutputAudioFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -117,11 +124,16 @@ import java.util.Objects;
|
||||||
if (!mixer.isEnded()) {
|
if (!mixer.isEnded()) {
|
||||||
feedMixer();
|
feedMixer();
|
||||||
}
|
}
|
||||||
if (currentOutput.hasRemaining()) {
|
if (!mixerOutput.hasRemaining()) {
|
||||||
return currentOutput;
|
mixerOutput = mixer.getOutput();
|
||||||
}
|
}
|
||||||
currentOutput = mixer.getOutput();
|
|
||||||
return currentOutput;
|
if (audioProcessingPipeline.isOperational()) {
|
||||||
|
feedProcessingPipelineFromMixer();
|
||||||
|
return audioProcessingPipeline.getOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mixerOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resets the graph to an unconfigured state, releasing any underlying resources. */
|
/** Resets the graph to an unconfigured state, releasing any underlying resources. */
|
||||||
|
|
@ -131,15 +143,31 @@ import java.util.Objects;
|
||||||
}
|
}
|
||||||
inputs.clear();
|
inputs.clear();
|
||||||
mixer.reset();
|
mixer.reset();
|
||||||
|
audioProcessingPipeline.reset();
|
||||||
|
|
||||||
finishedInputs = 0;
|
finishedInputs = 0;
|
||||||
currentOutput = EMPTY_BUFFER;
|
mixerOutput = EMPTY_BUFFER;
|
||||||
outputAudioFormat = AudioFormat.NOT_SET;
|
mixerAudioFormat = AudioFormat.NOT_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether the input has ended and all queued data has been output. */
|
/** Returns whether the input has ended and all queued data has been output. */
|
||||||
public boolean isEnded() {
|
public boolean isEnded() {
|
||||||
return !currentOutput.hasRemaining() && finishedInputs >= inputs.size() && mixer.isEnded();
|
if (audioProcessingPipeline.isOperational()) {
|
||||||
|
return audioProcessingPipeline.isEnded();
|
||||||
|
}
|
||||||
|
return isMixerEnded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMixerEnded() {
|
||||||
|
return !mixerOutput.hasRemaining() && finishedInputs >= inputs.size() && mixer.isEnded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void feedProcessingPipelineFromMixer() {
|
||||||
|
if (isMixerEnded()) {
|
||||||
|
audioProcessingPipeline.queueEndOfStream();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
audioProcessingPipeline.queueInput(mixerOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void feedMixer() throws ExportException {
|
private void feedMixer() throws ExportException {
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,11 @@ import static java.lang.Math.min;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.audio.AudioProcessor;
|
||||||
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
|
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.decoder.DecoderInputBuffer;
|
import androidx.media3.decoder.DecoderInputBuffer;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import org.checkerframework.dataflow.qual.Pure;
|
import org.checkerframework.dataflow.qual.Pure;
|
||||||
|
|
||||||
|
|
@ -50,13 +52,14 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
Format firstInputFormat,
|
Format firstInputFormat,
|
||||||
TransformationRequest transformationRequest,
|
TransformationRequest transformationRequest,
|
||||||
EditedMediaItem firstEditedMediaItem,
|
EditedMediaItem firstEditedMediaItem,
|
||||||
|
ImmutableList<AudioProcessor> compositionAudioProcessors,
|
||||||
AudioMixer.Factory mixerFactory,
|
AudioMixer.Factory mixerFactory,
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
MuxerWrapper muxerWrapper,
|
MuxerWrapper muxerWrapper,
|
||||||
FallbackListener fallbackListener)
|
FallbackListener fallbackListener)
|
||||||
throws ExportException {
|
throws ExportException {
|
||||||
super(firstAssetLoaderTrackFormat, muxerWrapper);
|
super(firstAssetLoaderTrackFormat, muxerWrapper);
|
||||||
audioGraph = new AudioGraph(mixerFactory);
|
audioGraph = new AudioGraph(mixerFactory, compositionAudioProcessors);
|
||||||
this.firstInputFormat = firstInputFormat;
|
this.firstInputFormat = firstInputFormat;
|
||||||
firstInput = audioGraph.registerInput(firstEditedMediaItem, firstInputFormat);
|
firstInput = audioGraph.registerInput(firstEditedMediaItem, firstInputFormat);
|
||||||
encoderInputAudioFormat = audioGraph.getOutputAudioFormat();
|
encoderInputAudioFormat = audioGraph.getOutputAudioFormat();
|
||||||
|
|
|
||||||
|
|
@ -950,8 +950,6 @@ public final class Transformer {
|
||||||
* <p>This method is under development. A {@link Composition} must meet the following conditions:
|
* <p>This method is under development. A {@link Composition} must meet the following conditions:
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>The {@linkplain Composition#effects composition effects} must contain no {@linkplain
|
|
||||||
* Effects#audioProcessors audio effects}.
|
|
||||||
* <li>The video composition {@link Presentation} effect is applied after input streams are
|
* <li>The video composition {@link Presentation} effect is applied after input streams are
|
||||||
* composited. Other composition effects are ignored.
|
* composited. Other composition effects are ignored.
|
||||||
* </ul>
|
* </ul>
|
||||||
|
|
@ -1539,7 +1537,6 @@ public final class Transformer {
|
||||||
ComponentListener componentListener,
|
ComponentListener componentListener,
|
||||||
long initialTimestampOffsetUs,
|
long initialTimestampOffsetUs,
|
||||||
boolean useDefaultAssetLoaderFactory) {
|
boolean useDefaultAssetLoaderFactory) {
|
||||||
checkArgument(composition.effects.audioProcessors.isEmpty());
|
|
||||||
checkState(transformerInternal == null, "There is already an export in progress.");
|
checkState(transformerInternal == null, "There is already an export in progress.");
|
||||||
TransformationRequest transformationRequest = this.transformationRequest;
|
TransformationRequest transformationRequest = this.transformationRequest;
|
||||||
if (composition.hdrMode != Composition.HDR_MODE_KEEP_HDR) {
|
if (composition.hdrMode != Composition.HDR_MODE_KEEP_HDR) {
|
||||||
|
|
|
||||||
|
|
@ -655,6 +655,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
/* firstInputFormat= */ assetLoaderOutputFormat,
|
/* firstInputFormat= */ assetLoaderOutputFormat,
|
||||||
transformationRequest,
|
transformationRequest,
|
||||||
firstEditedMediaItem,
|
firstEditedMediaItem,
|
||||||
|
composition.effects.audioProcessors,
|
||||||
audioMixerFactory,
|
audioMixerFactory,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
muxerWrapper,
|
muxerWrapper,
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,9 @@ import com.google.common.collect.ImmutableList;
|
||||||
if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) {
|
if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!composition.effects.audioProcessors.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,9 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
|
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
|
||||||
|
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
@ -44,7 +46,7 @@ public class AudioGraphTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void silentItem_outputsCorrectAmountOfBytes() throws Exception {
|
public void silentItem_outputsCorrectAmountOfBytes() throws Exception {
|
||||||
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory());
|
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of());
|
||||||
|
|
||||||
GraphInput input = audioGraph.registerInput(FAKE_ITEM, getPcmFormat(SURROUND_50000));
|
GraphInput input = audioGraph.registerInput(FAKE_ITEM, getPcmFormat(SURROUND_50000));
|
||||||
input.onMediaItemChanged(
|
input.onMediaItemChanged(
|
||||||
|
|
@ -56,16 +58,33 @@ public class AudioGraphTest {
|
||||||
assertThat(bytesOutput).isEqualTo(3 * 50_000 * 2 * 6);
|
assertThat(bytesOutput).isEqualTo(3 * 50_000 * 2 * 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void silentItem_withSampleRateChange_outputsCorrectAmountOfBytes() throws Exception {
|
||||||
|
SonicAudioProcessor changeTo100000Hz = new SonicAudioProcessor();
|
||||||
|
changeTo100000Hz.setOutputSampleRateHz(100_000);
|
||||||
|
AudioGraph audioGraph =
|
||||||
|
new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of(changeTo100000Hz));
|
||||||
|
|
||||||
|
GraphInput input = audioGraph.registerInput(FAKE_ITEM, getPcmFormat(SURROUND_50000));
|
||||||
|
input.onMediaItemChanged(
|
||||||
|
FAKE_ITEM, /* durationUs= */ 3_000_000, /* trackFormat= */ null, /* isLast= */ true);
|
||||||
|
int bytesOutput = drainAudioGraph(audioGraph);
|
||||||
|
|
||||||
|
// 3 second stream with 100_000 frames per second.
|
||||||
|
// 16 bit PCM has 2 bytes per channel.
|
||||||
|
assertThat(bytesOutput).isEqualTo(3 * 100_000 * 2 * 6);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getOutputAudioFormat_afterInitialization_isNotSet() throws Exception {
|
public void getOutputAudioFormat_afterInitialization_isNotSet() throws Exception {
|
||||||
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory());
|
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of());
|
||||||
|
|
||||||
assertThat(audioGraph.getOutputAudioFormat()).isEqualTo(AudioFormat.NOT_SET);
|
assertThat(audioGraph.getOutputAudioFormat()).isEqualTo(AudioFormat.NOT_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getOutputAudioFormat_afterRegisterInput_matchesInputFormat() throws Exception {
|
public void getOutputAudioFormat_afterRegisterInput_matchesInputFormat() throws Exception {
|
||||||
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory());
|
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of());
|
||||||
|
|
||||||
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(MONO_48000));
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(MONO_48000));
|
||||||
|
|
||||||
|
|
@ -74,18 +93,18 @@ public class AudioGraphTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getOutputAudioFormat_afterConfigure_matchesConfiguredFormat() throws Exception {
|
public void getOutputAudioFormat_afterConfigure_matchesConfiguredFormat() throws Exception {
|
||||||
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory());
|
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of());
|
||||||
|
|
||||||
audioGraph.configure(SURROUND_50000);
|
audioGraph.configure(/* mixerAudioFormat= */ SURROUND_50000);
|
||||||
|
|
||||||
assertThat(audioGraph.getOutputAudioFormat()).isEqualTo(SURROUND_50000);
|
assertThat(audioGraph.getOutputAudioFormat()).isEqualTo(SURROUND_50000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void registerInput_afterConfigure_doesNotChangeOutputFormat() throws Exception {
|
public void registerInput_afterConfigure_doesNotChangeOutputFormat() throws Exception {
|
||||||
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory());
|
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of());
|
||||||
|
|
||||||
audioGraph.configure(STEREO_44100);
|
audioGraph.configure(/* mixerAudioFormat= */ STEREO_44100);
|
||||||
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(STEREO_48000));
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(STEREO_48000));
|
||||||
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(MONO_44100));
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(MONO_44100));
|
||||||
|
|
||||||
|
|
@ -94,7 +113,7 @@ public class AudioGraphTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void registerInput_afterRegisterInput_doesNotChangeOutputFormat() throws Exception {
|
public void registerInput_afterRegisterInput_doesNotChangeOutputFormat() throws Exception {
|
||||||
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory());
|
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of());
|
||||||
|
|
||||||
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(STEREO_48000));
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(STEREO_48000));
|
||||||
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(MONO_44100));
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(MONO_44100));
|
||||||
|
|
@ -102,6 +121,56 @@ public class AudioGraphTest {
|
||||||
assertThat(audioGraph.getOutputAudioFormat()).isEqualTo(STEREO_48000);
|
assertThat(audioGraph.getOutputAudioFormat()).isEqualTo(STEREO_48000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registerInput_afterReset_changesOutputFormat() throws Exception {
|
||||||
|
AudioGraph audioGraph = new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of());
|
||||||
|
|
||||||
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(STEREO_48000));
|
||||||
|
audioGraph.reset();
|
||||||
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(MONO_44100));
|
||||||
|
|
||||||
|
assertThat(audioGraph.getOutputAudioFormat()).isEqualTo(MONO_44100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configure_withAudioProcessor_affectsOutputFormat() throws Exception {
|
||||||
|
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
||||||
|
sonicAudioProcessor.setOutputSampleRateHz(48_000);
|
||||||
|
AudioGraph audioGraph =
|
||||||
|
new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of(sonicAudioProcessor));
|
||||||
|
|
||||||
|
audioGraph.configure(/* mixerAudioFormat= */ SURROUND_50000);
|
||||||
|
|
||||||
|
assertThat(audioGraph.getOutputAudioFormat().sampleRate).isEqualTo(48_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registerInput_withAudioProcessor_affectsOutputFormat() throws Exception {
|
||||||
|
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
||||||
|
sonicAudioProcessor.setOutputSampleRateHz(48_000);
|
||||||
|
AudioGraph audioGraph =
|
||||||
|
new AudioGraph(new DefaultAudioMixer.Factory(), ImmutableList.of(sonicAudioProcessor));
|
||||||
|
|
||||||
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(SURROUND_50000));
|
||||||
|
|
||||||
|
assertThat(audioGraph.getOutputAudioFormat().sampleRate).isEqualTo(48_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registerInput_withMultipleAudioProcessors_affectsOutputFormat() throws Exception {
|
||||||
|
SonicAudioProcessor changeTo96000Hz = new SonicAudioProcessor();
|
||||||
|
changeTo96000Hz.setOutputSampleRateHz(96_000);
|
||||||
|
SonicAudioProcessor changeTo48000Hz = new SonicAudioProcessor();
|
||||||
|
changeTo48000Hz.setOutputSampleRateHz(48_000);
|
||||||
|
AudioGraph audioGraph =
|
||||||
|
new AudioGraph(
|
||||||
|
new DefaultAudioMixer.Factory(), ImmutableList.of(changeTo96000Hz, changeTo48000Hz));
|
||||||
|
|
||||||
|
audioGraph.registerInput(FAKE_ITEM, getPcmFormat(SURROUND_50000));
|
||||||
|
|
||||||
|
assertThat(audioGraph.getOutputAudioFormat().sampleRate).isEqualTo(48_000);
|
||||||
|
}
|
||||||
|
|
||||||
/** Drains the graph and returns the number of bytes output. */
|
/** Drains the graph and returns the number of bytes output. */
|
||||||
private static int drainAudioGraph(AudioGraph audioGraph) throws ExportException {
|
private static int drainAudioGraph(AudioGraph audioGraph) throws ExportException {
|
||||||
int bytesOutput = 0;
|
int bytesOutput = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue