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

View File

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