Avoid input/output copies in SonicAudioProcessor.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=151431376
This commit is contained in:
andrewlewis 2017-03-28 03:27:32 -07:00 committed by Oliver Woodman
parent 422c2d0ab7
commit 1dcfae452a
2 changed files with 48 additions and 71 deletions

View File

@ -17,6 +17,7 @@
package com.google.android.exoplayer2.audio;
import com.google.android.exoplayer2.util.Assertions;
import java.nio.ShortBuffer;
import java.util.Arrays;
/**
@ -112,60 +113,39 @@ import java.util.Arrays;
}
/**
* Writes {@code numBytes} from {@code buffer} as input.
* Queues remaining data from {@code buffer}, and advances its position by the number of bytes
* consumed.
*
* @param buffer A buffer containing input data.
* @param numBytes The number of bytes of input data to read from {@code buffer}.
* @param buffer A {@link ShortBuffer} containing input data between its position and limit.
*/
public void writeBytesToStream(byte[] buffer, int numBytes) {
int numSamples = numBytes / (2 * numChannels);
short sample;
enlargeInputBufferIfNeeded(numSamples);
int xBuffer = numInputSamples * numChannels;
for (int xByte = 0; xByte + 1 < numBytes; xByte += 2) {
sample = (short) ((buffer[xByte] & 0xff) | (buffer[xByte + 1] << 8));
inputBuffer[xBuffer++] = sample;
}
numInputSamples += numSamples;
public void queueInput(ShortBuffer buffer) {
int samplesToWrite = buffer.remaining() / numChannels;
int bytesToWrite = samplesToWrite * numChannels * 2;
enlargeInputBufferIfNeeded(samplesToWrite);
buffer.get(inputBuffer, numInputSamples * numChannels, bytesToWrite / 2);
numInputSamples += samplesToWrite;
processStreamInput();
}
/**
* Reads up to {@code maxBytes} of output into {@code buffer}.
* Gets available output, outputting to the start of {@code buffer}. The buffer's position will be
* advanced by the number of bytes written.
*
* @param buffer The buffer into which output will be written.
* @param maxBytes The maximum number of bytes to write.
* @return The number of bytes read from the stream.
* @param buffer A {@link ShortBuffer} into which output will be written.
*/
public int readBytesFromStream(byte[] buffer, int maxBytes) {
int maxSamples = maxBytes / (2 * numChannels);
int numSamples = numOutputSamples;
int remainingSamples = 0;
if (numSamples == 0 || maxSamples == 0) {
return 0;
}
if (numSamples > maxSamples) {
remainingSamples = numSamples - maxSamples;
numSamples = maxSamples;
}
for (int xSample = 0; xSample < numSamples * numChannels; xSample++) {
short sample = outputBuffer[xSample];
buffer[xSample << 1] = (byte) (sample & 0xff);
buffer[(xSample << 1) + 1] = (byte) (sample >> 8);
}
System.arraycopy(outputBuffer, numSamples * numChannels, outputBuffer, 0,
remainingSamples * numChannels);
numOutputSamples = remainingSamples;
return 2 * numSamples * numChannels;
public void getOutput(ShortBuffer buffer) {
int samplesToRead = Math.min(buffer.remaining() / numChannels, numOutputSamples);
buffer.put(outputBuffer, 0, samplesToRead * numChannels);
numOutputSamples -= samplesToRead;
System.arraycopy(outputBuffer, samplesToRead * numChannels, outputBuffer, 0,
numOutputSamples * numChannels);
}
/**
* Forces generating output using whatever data has been queued already. No extra delay will be
* added to the output, but flushing in the middle of words could introduce distortion.
*/
public void flushStream() {
public void queueEndOfStream() {
int remainingSamples = numInputSamples;
float s = speed / pitch;
int expectedOutputSamples =
@ -189,10 +169,9 @@ import java.util.Arrays;
}
/**
* Returns the number of output samples that can be read with
* {@link #readBytesFromStream(byte[], int)}.
* Returns the number of output samples that can be read with {@link #getOutput(ShortBuffer)}.
*/
public int samplesAvailable() {
public int getSamplesAvailable() {
return numOutputSamples;
}

View File

@ -21,6 +21,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
/**
* An {@link AudioProcessor} that uses the Sonic library to modify the speed/pitch of audio.
@ -50,8 +51,6 @@ import java.nio.ByteOrder;
*/
private static final float CLOSE_THRESHOLD = 0.01f;
private static final byte[] EMPTY_ARRAY = new byte[0];
private int channelCount;
private int sampleRateHz;
@ -59,9 +58,9 @@ import java.nio.ByteOrder;
private float speed;
private float pitch;
private byte[] inputArray;
private ShortBuffer shortBuffer;
private ByteBuffer buffer;
private byte[] bufferArray;
private ByteBuffer outputBuffer;
private long inputBytes;
private long outputBytes;
@ -76,9 +75,8 @@ import java.nio.ByteOrder;
channelCount = Format.NO_VALUE;
sampleRateHz = Format.NO_VALUE;
buffer = EMPTY_BUFFER;
shortBuffer = buffer.asShortBuffer();
outputBuffer = EMPTY_BUFFER;
inputArray = EMPTY_ARRAY;
bufferArray = EMPTY_ARRAY;
}
/**
@ -142,31 +140,32 @@ import java.nio.ByteOrder;
@Override
public void queueInput(ByteBuffer inputBuffer) {
// TODO: Remove this extra copy.
int inputBytesToRead = inputBuffer.remaining();
if (inputArray == null || inputArray.length < inputBytesToRead) {
inputArray = new byte[inputBytesToRead];
if (inputBuffer.hasRemaining()) {
ShortBuffer shortBuffer = inputBuffer.asShortBuffer();
int inputSize = inputBuffer.remaining();
inputBytes += inputSize;
sonic.queueInput(shortBuffer);
inputBuffer.position(inputBuffer.position() + inputSize);
}
inputBuffer.get(inputArray, 0, inputBytesToRead);
sonic.writeBytesToStream(inputArray, inputBytesToRead);
int outputSize = sonic.samplesAvailable() * channelCount * 2;
if (buffer.capacity() < outputSize) {
buffer = ByteBuffer.allocateDirect(outputSize).order(ByteOrder.nativeOrder());
bufferArray = new byte[outputSize];
} else {
buffer.clear();
int outputSize = sonic.getSamplesAvailable() * channelCount * 2;
if (outputSize > 0) {
if (buffer.capacity() < outputSize) {
buffer = ByteBuffer.allocateDirect(outputSize).order(ByteOrder.nativeOrder());
shortBuffer = buffer.asShortBuffer();
} else {
buffer.clear();
shortBuffer.clear();
}
sonic.getOutput(shortBuffer);
outputBytes += outputSize;
buffer.limit(outputSize);
outputBuffer = buffer;
}
inputBytes += inputBytesToRead;
int outputBytesRead = sonic.readBytesFromStream(bufferArray, outputSize);
buffer.put(bufferArray, 0, outputBytesRead);
buffer.flip();
outputBytes += outputSize;
outputBuffer = buffer;
}
@Override
public void queueEndOfStream() {
sonic.flushStream();
sonic.queueEndOfStream();
inputEnded = true;
}
@ -179,7 +178,7 @@ import java.nio.ByteOrder;
@Override
public boolean isEnded() {
return inputEnded && (sonic == null || sonic.samplesAvailable() == 0);
return inputEnded && (sonic == null || sonic.getSamplesAvailable() == 0);
}
@Override
@ -198,8 +197,7 @@ import java.nio.ByteOrder;
sonic = null;
buffer = EMPTY_BUFFER;
outputBuffer = EMPTY_BUFFER;
inputArray = EMPTY_ARRAY;
bufferArray = EMPTY_ARRAY;
shortBuffer = null;
}
}