mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Use audio frames vs samples consistently in Sonic
In audio processors an audio frame consists of a sample (which is 2 bytes for 16-bit PCM) for each channel. Sonic used "sample" to refer to this. We've already diverged from the original source for Sonic quite a bit (deleting code and making stylistic changes) and there haven't been upstream changes so far, so it seems fine to start making more substantial changes here. There should be no behavior changes here. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190916793
This commit is contained in:
parent
2b9b2510dc
commit
017c95ffdd
@ -32,16 +32,16 @@ import java.util.Arrays;
|
||||
private static final int AMDF_FREQUENCY = 4000;
|
||||
|
||||
private final int inputSampleRateHz;
|
||||
private final int numChannels;
|
||||
private final int channelCount;
|
||||
private final float speed;
|
||||
private final float pitch;
|
||||
private final float rate;
|
||||
private final int minPeriod;
|
||||
private final int maxPeriod;
|
||||
private final int maxRequired;
|
||||
private final int maxRequiredFrameCount;
|
||||
private final short[] downSampleBuffer;
|
||||
|
||||
private int inputBufferSize;
|
||||
private int inputBufferSizeFrames;
|
||||
private short[] inputBuffer;
|
||||
private int outputBufferSize;
|
||||
private short[] outputBuffer;
|
||||
@ -49,10 +49,10 @@ import java.util.Arrays;
|
||||
private short[] pitchBuffer;
|
||||
private int oldRatePosition;
|
||||
private int newRatePosition;
|
||||
private int numInputSamples;
|
||||
private int numOutputSamples;
|
||||
private int numPitchSamples;
|
||||
private int remainingInputToCopy;
|
||||
private int inputFrameCount;
|
||||
private int outputFrameCount;
|
||||
private int pitchFrameCount;
|
||||
private int remainingInputToCopyFrameCount;
|
||||
private int prevPeriod;
|
||||
private int prevMinDiff;
|
||||
private int minDiff;
|
||||
@ -62,25 +62,25 @@ import java.util.Arrays;
|
||||
* Creates a new Sonic audio stream processor.
|
||||
*
|
||||
* @param inputSampleRateHz The sample rate of input audio, in hertz.
|
||||
* @param numChannels The number of channels in the input audio.
|
||||
* @param channelCount The number of channels in the input audio.
|
||||
* @param speed The speedup factor for output audio.
|
||||
* @param pitch The pitch factor for output audio.
|
||||
* @param outputSampleRateHz The sample rate for output audio, in hertz.
|
||||
*/
|
||||
public Sonic(int inputSampleRateHz, int numChannels, float speed, float pitch,
|
||||
int outputSampleRateHz) {
|
||||
public Sonic(
|
||||
int inputSampleRateHz, int channelCount, float speed, float pitch, int outputSampleRateHz) {
|
||||
this.inputSampleRateHz = inputSampleRateHz;
|
||||
this.numChannels = numChannels;
|
||||
this.channelCount = channelCount;
|
||||
minPeriod = inputSampleRateHz / MAXIMUM_PITCH;
|
||||
maxPeriod = inputSampleRateHz / MINIMUM_PITCH;
|
||||
maxRequired = 2 * maxPeriod;
|
||||
downSampleBuffer = new short[maxRequired];
|
||||
inputBufferSize = maxRequired;
|
||||
inputBuffer = new short[maxRequired * numChannels];
|
||||
outputBufferSize = maxRequired;
|
||||
outputBuffer = new short[maxRequired * numChannels];
|
||||
pitchBufferSize = maxRequired;
|
||||
pitchBuffer = new short[maxRequired * numChannels];
|
||||
maxRequiredFrameCount = 2 * maxPeriod;
|
||||
downSampleBuffer = new short[maxRequiredFrameCount];
|
||||
inputBufferSizeFrames = maxRequiredFrameCount;
|
||||
inputBuffer = new short[maxRequiredFrameCount * channelCount];
|
||||
outputBufferSize = maxRequiredFrameCount;
|
||||
outputBuffer = new short[maxRequiredFrameCount * channelCount];
|
||||
pitchBufferSize = maxRequiredFrameCount;
|
||||
pitchBuffer = new short[maxRequiredFrameCount * channelCount];
|
||||
oldRatePosition = 0;
|
||||
newRatePosition = 0;
|
||||
prevPeriod = 0;
|
||||
@ -96,11 +96,11 @@ import java.util.Arrays;
|
||||
* @param buffer A {@link ShortBuffer} containing input data between its position and limit.
|
||||
*/
|
||||
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;
|
||||
int framesToWrite = buffer.remaining() / channelCount;
|
||||
int bytesToWrite = framesToWrite * channelCount * 2;
|
||||
enlargeInputBufferIfNeeded(framesToWrite);
|
||||
buffer.get(inputBuffer, inputFrameCount * channelCount, bytesToWrite / 2);
|
||||
inputFrameCount += framesToWrite;
|
||||
processStreamInput();
|
||||
}
|
||||
|
||||
@ -111,11 +111,15 @@ import java.util.Arrays;
|
||||
* @param buffer A {@link ShortBuffer} into which output will be written.
|
||||
*/
|
||||
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);
|
||||
int framesToRead = Math.min(buffer.remaining() / channelCount, outputFrameCount);
|
||||
buffer.put(outputBuffer, 0, framesToRead * channelCount);
|
||||
outputFrameCount -= framesToRead;
|
||||
System.arraycopy(
|
||||
outputBuffer,
|
||||
framesToRead * channelCount,
|
||||
outputBuffer,
|
||||
0,
|
||||
outputFrameCount * channelCount);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,80 +127,82 @@ import java.util.Arrays;
|
||||
* added to the output, but flushing in the middle of words could introduce distortion.
|
||||
*/
|
||||
public void queueEndOfStream() {
|
||||
int remainingSamples = numInputSamples;
|
||||
int remainingFrameCount = inputFrameCount;
|
||||
float s = speed / pitch;
|
||||
float r = rate * pitch;
|
||||
int expectedOutputSamples =
|
||||
numOutputSamples + (int) ((remainingSamples / s + numPitchSamples) / r + 0.5f);
|
||||
int expectedOutputFrames =
|
||||
outputFrameCount + (int) ((remainingFrameCount / s + pitchFrameCount) / r + 0.5f);
|
||||
|
||||
// Add enough silence to flush both input and pitch buffers.
|
||||
enlargeInputBufferIfNeeded(remainingSamples + 2 * maxRequired);
|
||||
for (int xSample = 0; xSample < 2 * maxRequired * numChannels; xSample++) {
|
||||
inputBuffer[remainingSamples * numChannels + xSample] = 0;
|
||||
enlargeInputBufferIfNeeded(remainingFrameCount + 2 * maxRequiredFrameCount);
|
||||
for (int xSample = 0; xSample < 2 * maxRequiredFrameCount * channelCount; xSample++) {
|
||||
inputBuffer[remainingFrameCount * channelCount + xSample] = 0;
|
||||
}
|
||||
numInputSamples += 2 * maxRequired;
|
||||
inputFrameCount += 2 * maxRequiredFrameCount;
|
||||
processStreamInput();
|
||||
// Throw away any extra samples we generated due to the silence we added.
|
||||
if (numOutputSamples > expectedOutputSamples) {
|
||||
numOutputSamples = expectedOutputSamples;
|
||||
// Throw away any extra frames we generated due to the silence we added.
|
||||
if (outputFrameCount > expectedOutputFrames) {
|
||||
outputFrameCount = expectedOutputFrames;
|
||||
}
|
||||
// Empty input and pitch buffers.
|
||||
numInputSamples = 0;
|
||||
remainingInputToCopy = 0;
|
||||
numPitchSamples = 0;
|
||||
inputFrameCount = 0;
|
||||
remainingInputToCopyFrameCount = 0;
|
||||
pitchFrameCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of output samples that can be read with {@link #getOutput(ShortBuffer)}.
|
||||
*/
|
||||
public int getSamplesAvailable() {
|
||||
return numOutputSamples;
|
||||
/** Returns the number of output frames that can be read with {@link #getOutput(ShortBuffer)}. */
|
||||
public int getFramesAvailable() {
|
||||
return outputFrameCount;
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void enlargeOutputBufferIfNeeded(int numSamples) {
|
||||
if (numOutputSamples + numSamples > outputBufferSize) {
|
||||
outputBufferSize += (outputBufferSize / 2) + numSamples;
|
||||
outputBuffer = Arrays.copyOf(outputBuffer, outputBufferSize * numChannels);
|
||||
private void enlargeOutputBufferIfNeeded(int frameCount) {
|
||||
if (outputFrameCount + frameCount > outputBufferSize) {
|
||||
outputBufferSize += (outputBufferSize / 2) + frameCount;
|
||||
outputBuffer = Arrays.copyOf(outputBuffer, outputBufferSize * channelCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void enlargeInputBufferIfNeeded(int numSamples) {
|
||||
if (numInputSamples + numSamples > inputBufferSize) {
|
||||
inputBufferSize += (inputBufferSize / 2) + numSamples;
|
||||
inputBuffer = Arrays.copyOf(inputBuffer, inputBufferSize * numChannels);
|
||||
private void enlargeInputBufferIfNeeded(int frameCount) {
|
||||
if (inputFrameCount + frameCount > inputBufferSizeFrames) {
|
||||
inputBufferSizeFrames += (inputBufferSizeFrames / 2) + frameCount;
|
||||
inputBuffer = Arrays.copyOf(inputBuffer, inputBufferSizeFrames * channelCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeProcessedInputSamples(int position) {
|
||||
int remainingSamples = numInputSamples - position;
|
||||
System.arraycopy(inputBuffer, position * numChannels, inputBuffer, 0,
|
||||
remainingSamples * numChannels);
|
||||
numInputSamples = remainingSamples;
|
||||
private void removeProcessedInputFrames(int positionFrames) {
|
||||
int remainingFrames = inputFrameCount - positionFrames;
|
||||
System.arraycopy(
|
||||
inputBuffer, positionFrames * channelCount, inputBuffer, 0, remainingFrames * channelCount);
|
||||
inputFrameCount = remainingFrames;
|
||||
}
|
||||
|
||||
private void copyToOutput(short[] samples, int position, int numSamples) {
|
||||
enlargeOutputBufferIfNeeded(numSamples);
|
||||
System.arraycopy(samples, position * numChannels, outputBuffer, numOutputSamples * numChannels,
|
||||
numSamples * numChannels);
|
||||
numOutputSamples += numSamples;
|
||||
private void copyToOutput(short[] samples, int positionFrames, int frameCount) {
|
||||
enlargeOutputBufferIfNeeded(frameCount);
|
||||
System.arraycopy(
|
||||
samples,
|
||||
positionFrames * channelCount,
|
||||
outputBuffer,
|
||||
outputFrameCount * channelCount,
|
||||
frameCount * channelCount);
|
||||
outputFrameCount += frameCount;
|
||||
}
|
||||
|
||||
private int copyInputToOutput(int position) {
|
||||
int numSamples = Math.min(maxRequired, remainingInputToCopy);
|
||||
copyToOutput(inputBuffer, position, numSamples);
|
||||
remainingInputToCopy -= numSamples;
|
||||
return numSamples;
|
||||
private int copyInputToOutput(int positionFrames) {
|
||||
int frameCount = Math.min(maxRequiredFrameCount, remainingInputToCopyFrameCount);
|
||||
copyToOutput(inputBuffer, positionFrames, frameCount);
|
||||
remainingInputToCopyFrameCount -= frameCount;
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
private void downSampleInput(short[] samples, int position, int skip) {
|
||||
// If skip is greater than one, average skip samples together and write them to the down-sample
|
||||
// buffer. If numChannels is greater than one, mix the channels together as we down sample.
|
||||
int numSamples = maxRequired / skip;
|
||||
int samplesPerValue = numChannels * skip;
|
||||
position *= numChannels;
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
// buffer. If channelCount is greater than one, mix the channels together as we down sample.
|
||||
int frameCount = maxRequiredFrameCount / skip;
|
||||
int samplesPerValue = channelCount * skip;
|
||||
position *= channelCount;
|
||||
for (int i = 0; i < frameCount; i++) {
|
||||
int value = 0;
|
||||
for (int j = 0; j < samplesPerValue; j++) {
|
||||
value += samples[position + i * samplesPerValue + j];
|
||||
@ -213,7 +219,7 @@ import java.util.Arrays;
|
||||
int worstPeriod = 255;
|
||||
int minDiff = 1;
|
||||
int maxDiff = 0;
|
||||
position *= numChannels;
|
||||
position *= channelCount;
|
||||
for (int period = minPeriod; period <= maxPeriod; period++) {
|
||||
int diff = 0;
|
||||
for (int i = 0; i < period; i++) {
|
||||
@ -242,28 +248,22 @@ import java.util.Arrays;
|
||||
* Returns whether the previous pitch period estimate is a better approximation, which can occur
|
||||
* at the abrupt end of voiced words.
|
||||
*/
|
||||
private boolean previousPeriodBetter(int minDiff, int maxDiff, boolean preferNewPeriod) {
|
||||
private boolean previousPeriodBetter(int minDiff, int maxDiff) {
|
||||
if (minDiff == 0 || prevPeriod == 0) {
|
||||
return false;
|
||||
}
|
||||
if (preferNewPeriod) {
|
||||
if (maxDiff > minDiff * 3) {
|
||||
// Got a reasonable match this period
|
||||
return false;
|
||||
}
|
||||
if (minDiff * 2 <= prevMinDiff * 3) {
|
||||
// Mismatch is not that much greater this period
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (minDiff <= prevMinDiff) {
|
||||
return false;
|
||||
}
|
||||
if (maxDiff > minDiff * 3) {
|
||||
// Got a reasonable match this period.
|
||||
return false;
|
||||
}
|
||||
if (minDiff * 2 <= prevMinDiff * 3) {
|
||||
// Mismatch is not that much greater this period.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int findPitchPeriod(short[] samples, int position, boolean preferNewPeriod) {
|
||||
private int findPitchPeriod(short[] samples, int position) {
|
||||
// Find the pitch period. This is a critical step, and we may have to try multiple ways to get a
|
||||
// good answer. This version uses AMDF. To improve speed, we down sample by an integer factor
|
||||
// get in the 11 kHz range, and then do it again with a narrower frequency range without down
|
||||
@ -271,7 +271,7 @@ import java.util.Arrays;
|
||||
int period;
|
||||
int retPeriod;
|
||||
int skip = inputSampleRateHz > AMDF_FREQUENCY ? inputSampleRateHz / AMDF_FREQUENCY : 1;
|
||||
if (numChannels == 1 && skip == 1) {
|
||||
if (channelCount == 1 && skip == 1) {
|
||||
period = findPitchPeriodInRange(samples, position, minPeriod, maxPeriod);
|
||||
} else {
|
||||
downSampleInput(samples, position, skip);
|
||||
@ -286,7 +286,7 @@ import java.util.Arrays;
|
||||
if (maxP > maxPeriod) {
|
||||
maxP = maxPeriod;
|
||||
}
|
||||
if (numChannels == 1) {
|
||||
if (channelCount == 1) {
|
||||
period = findPitchPeriodInRange(samples, position, minP, maxP);
|
||||
} else {
|
||||
downSampleInput(samples, position, 1);
|
||||
@ -294,7 +294,7 @@ import java.util.Arrays;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (previousPeriodBetter(minDiff, maxDiff, preferNewPeriod)) {
|
||||
if (previousPeriodBetter(minDiff, maxDiff)) {
|
||||
retPeriod = prevPeriod;
|
||||
} else {
|
||||
retPeriod = period;
|
||||
@ -304,30 +304,38 @@ import java.util.Arrays;
|
||||
return retPeriod;
|
||||
}
|
||||
|
||||
private void moveNewSamplesToPitchBuffer(int originalNumOutputSamples) {
|
||||
int numSamples = numOutputSamples - originalNumOutputSamples;
|
||||
if (numPitchSamples + numSamples > pitchBufferSize) {
|
||||
pitchBufferSize += (pitchBufferSize / 2) + numSamples;
|
||||
pitchBuffer = Arrays.copyOf(pitchBuffer, pitchBufferSize * numChannels);
|
||||
private void moveNewSamplesToPitchBuffer(int originalOutputFrameCount) {
|
||||
int frameCount = outputFrameCount - originalOutputFrameCount;
|
||||
if (pitchFrameCount + frameCount > pitchBufferSize) {
|
||||
pitchBufferSize += (pitchBufferSize / 2) + frameCount;
|
||||
pitchBuffer = Arrays.copyOf(pitchBuffer, pitchBufferSize * channelCount);
|
||||
}
|
||||
System.arraycopy(outputBuffer, originalNumOutputSamples * numChannels, pitchBuffer,
|
||||
numPitchSamples * numChannels, numSamples * numChannels);
|
||||
numOutputSamples = originalNumOutputSamples;
|
||||
numPitchSamples += numSamples;
|
||||
System.arraycopy(
|
||||
outputBuffer,
|
||||
originalOutputFrameCount * channelCount,
|
||||
pitchBuffer,
|
||||
pitchFrameCount * channelCount,
|
||||
frameCount * channelCount);
|
||||
outputFrameCount = originalOutputFrameCount;
|
||||
pitchFrameCount += frameCount;
|
||||
}
|
||||
|
||||
private void removePitchSamples(int numSamples) {
|
||||
if (numSamples == 0) {
|
||||
private void removePitchFrames(int frameCount) {
|
||||
if (frameCount == 0) {
|
||||
return;
|
||||
}
|
||||
System.arraycopy(pitchBuffer, numSamples * numChannels, pitchBuffer, 0,
|
||||
(numPitchSamples - numSamples) * numChannels);
|
||||
numPitchSamples -= numSamples;
|
||||
System.arraycopy(
|
||||
pitchBuffer,
|
||||
frameCount * channelCount,
|
||||
pitchBuffer,
|
||||
0,
|
||||
(pitchFrameCount - frameCount) * channelCount);
|
||||
pitchFrameCount -= frameCount;
|
||||
}
|
||||
|
||||
private short interpolate(short[] in, int inPos, int oldSampleRate, int newSampleRate) {
|
||||
short left = in[inPos];
|
||||
short right = in[inPos + numChannels];
|
||||
short right = in[inPos + channelCount];
|
||||
int position = newRatePosition * oldSampleRate;
|
||||
int leftPosition = oldRatePosition * newSampleRate;
|
||||
int rightPosition = (oldRatePosition + 1) * newSampleRate;
|
||||
@ -336,8 +344,8 @@ import java.util.Arrays;
|
||||
return (short) ((ratio * left + (width - ratio) * right) / width);
|
||||
}
|
||||
|
||||
private void adjustRate(float rate, int originalNumOutputSamples) {
|
||||
if (numOutputSamples == originalNumOutputSamples) {
|
||||
private void adjustRate(float rate, int originalOutputFrameCount) {
|
||||
if (outputFrameCount == originalOutputFrameCount) {
|
||||
return;
|
||||
}
|
||||
int newSampleRate = (int) (inputSampleRateHz / rate);
|
||||
@ -347,17 +355,17 @@ import java.util.Arrays;
|
||||
newSampleRate /= 2;
|
||||
oldSampleRate /= 2;
|
||||
}
|
||||
moveNewSamplesToPitchBuffer(originalNumOutputSamples);
|
||||
moveNewSamplesToPitchBuffer(originalOutputFrameCount);
|
||||
// Leave at least one pitch sample in the buffer.
|
||||
for (int position = 0; position < numPitchSamples - 1; position++) {
|
||||
for (int position = 0; position < pitchFrameCount - 1; position++) {
|
||||
while ((oldRatePosition + 1) * newSampleRate > newRatePosition * oldSampleRate) {
|
||||
enlargeOutputBufferIfNeeded(1);
|
||||
for (int i = 0; i < numChannels; i++) {
|
||||
outputBuffer[numOutputSamples * numChannels + i] =
|
||||
interpolate(pitchBuffer, position * numChannels + i, oldSampleRate, newSampleRate);
|
||||
for (int i = 0; i < channelCount; i++) {
|
||||
outputBuffer[outputFrameCount * channelCount + i] =
|
||||
interpolate(pitchBuffer, position * channelCount + i, oldSampleRate, newSampleRate);
|
||||
}
|
||||
newRatePosition++;
|
||||
numOutputSamples++;
|
||||
outputFrameCount++;
|
||||
}
|
||||
oldRatePosition++;
|
||||
if (oldRatePosition == oldSampleRate) {
|
||||
@ -366,91 +374,116 @@ import java.util.Arrays;
|
||||
newRatePosition = 0;
|
||||
}
|
||||
}
|
||||
removePitchSamples(numPitchSamples - 1);
|
||||
removePitchFrames(pitchFrameCount - 1);
|
||||
}
|
||||
|
||||
private int skipPitchPeriod(short[] samples, int position, float speed, int period) {
|
||||
// Skip over a pitch period, and copy period/speed samples to the output.
|
||||
int newSamples;
|
||||
int newFrameCount;
|
||||
if (speed >= 2.0f) {
|
||||
newSamples = (int) (period / (speed - 1.0f));
|
||||
newFrameCount = (int) (period / (speed - 1.0f));
|
||||
} else {
|
||||
newSamples = period;
|
||||
remainingInputToCopy = (int) (period * (2.0f - speed) / (speed - 1.0f));
|
||||
newFrameCount = period;
|
||||
remainingInputToCopyFrameCount = (int) (period * (2.0f - speed) / (speed - 1.0f));
|
||||
}
|
||||
enlargeOutputBufferIfNeeded(newSamples);
|
||||
overlapAdd(newSamples, numChannels, outputBuffer, numOutputSamples, samples, position, samples,
|
||||
enlargeOutputBufferIfNeeded(newFrameCount);
|
||||
overlapAdd(
|
||||
newFrameCount,
|
||||
channelCount,
|
||||
outputBuffer,
|
||||
outputFrameCount,
|
||||
samples,
|
||||
position,
|
||||
samples,
|
||||
position + period);
|
||||
numOutputSamples += newSamples;
|
||||
return newSamples;
|
||||
outputFrameCount += newFrameCount;
|
||||
return newFrameCount;
|
||||
}
|
||||
|
||||
private int insertPitchPeriod(short[] samples, int position, float speed, int period) {
|
||||
// Insert a pitch period, and determine how much input to copy directly.
|
||||
int newSamples;
|
||||
int newFrameCount;
|
||||
if (speed < 0.5f) {
|
||||
newSamples = (int) (period * speed / (1.0f - speed));
|
||||
newFrameCount = (int) (period * speed / (1.0f - speed));
|
||||
} else {
|
||||
newSamples = period;
|
||||
remainingInputToCopy = (int) (period * (2.0f * speed - 1.0f) / (1.0f - speed));
|
||||
newFrameCount = period;
|
||||
remainingInputToCopyFrameCount = (int) (period * (2.0f * speed - 1.0f) / (1.0f - speed));
|
||||
}
|
||||
enlargeOutputBufferIfNeeded(period + newSamples);
|
||||
System.arraycopy(samples, position * numChannels, outputBuffer, numOutputSamples * numChannels,
|
||||
period * numChannels);
|
||||
overlapAdd(newSamples, numChannels, outputBuffer, numOutputSamples + period, samples,
|
||||
position + period, samples, position);
|
||||
numOutputSamples += period + newSamples;
|
||||
return newSamples;
|
||||
enlargeOutputBufferIfNeeded(period + newFrameCount);
|
||||
System.arraycopy(
|
||||
samples,
|
||||
position * channelCount,
|
||||
outputBuffer,
|
||||
outputFrameCount * channelCount,
|
||||
period * channelCount);
|
||||
overlapAdd(
|
||||
newFrameCount,
|
||||
channelCount,
|
||||
outputBuffer,
|
||||
outputFrameCount + period,
|
||||
samples,
|
||||
position + period,
|
||||
samples,
|
||||
position);
|
||||
outputFrameCount += period + newFrameCount;
|
||||
return newFrameCount;
|
||||
}
|
||||
|
||||
private void changeSpeed(float speed) {
|
||||
if (numInputSamples < maxRequired) {
|
||||
if (inputFrameCount < maxRequiredFrameCount) {
|
||||
return;
|
||||
}
|
||||
int numSamples = numInputSamples;
|
||||
int position = 0;
|
||||
int frameCount = inputFrameCount;
|
||||
int positionFrames = 0;
|
||||
do {
|
||||
if (remainingInputToCopy > 0) {
|
||||
position += copyInputToOutput(position);
|
||||
if (remainingInputToCopyFrameCount > 0) {
|
||||
positionFrames += copyInputToOutput(positionFrames);
|
||||
} else {
|
||||
int period = findPitchPeriod(inputBuffer, position, true);
|
||||
int period = findPitchPeriod(inputBuffer, positionFrames);
|
||||
if (speed > 1.0) {
|
||||
position += period + skipPitchPeriod(inputBuffer, position, speed, period);
|
||||
positionFrames += period + skipPitchPeriod(inputBuffer, positionFrames, speed, period);
|
||||
} else {
|
||||
position += insertPitchPeriod(inputBuffer, position, speed, period);
|
||||
positionFrames += insertPitchPeriod(inputBuffer, positionFrames, speed, period);
|
||||
}
|
||||
}
|
||||
} while (position + maxRequired <= numSamples);
|
||||
removeProcessedInputSamples(position);
|
||||
} while (positionFrames + maxRequiredFrameCount <= frameCount);
|
||||
removeProcessedInputFrames(positionFrames);
|
||||
}
|
||||
|
||||
private void processStreamInput() {
|
||||
// Resample as many pitch periods as we have buffered on the input.
|
||||
int originalNumOutputSamples = numOutputSamples;
|
||||
int originalOutputFrameCount = outputFrameCount;
|
||||
float s = speed / pitch;
|
||||
float r = rate * pitch;
|
||||
if (s > 1.00001 || s < 0.99999) {
|
||||
changeSpeed(s);
|
||||
} else {
|
||||
copyToOutput(inputBuffer, 0, numInputSamples);
|
||||
numInputSamples = 0;
|
||||
copyToOutput(inputBuffer, 0, inputFrameCount);
|
||||
inputFrameCount = 0;
|
||||
}
|
||||
if (r != 1.0f) {
|
||||
adjustRate(r, originalNumOutputSamples);
|
||||
adjustRate(r, originalOutputFrameCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static void overlapAdd(int numSamples, int numChannels, short[] out, int outPos,
|
||||
short[] rampDown, int rampDownPos, short[] rampUp, int rampUpPos) {
|
||||
for (int i = 0; i < numChannels; i++) {
|
||||
int o = outPos * numChannels + i;
|
||||
int u = rampUpPos * numChannels + i;
|
||||
int d = rampDownPos * numChannels + i;
|
||||
for (int t = 0; t < numSamples; t++) {
|
||||
out[o] = (short) ((rampDown[d] * (numSamples - t) + rampUp[u] * t) / numSamples);
|
||||
o += numChannels;
|
||||
d += numChannels;
|
||||
u += numChannels;
|
||||
private static void overlapAdd(
|
||||
int frameCount,
|
||||
int channelCount,
|
||||
short[] out,
|
||||
int outPosition,
|
||||
short[] rampDown,
|
||||
int rampDownPosition,
|
||||
short[] rampUp,
|
||||
int rampUpPosition) {
|
||||
for (int i = 0; i < channelCount; i++) {
|
||||
int o = outPosition * channelCount + i;
|
||||
int u = rampUpPosition * channelCount + i;
|
||||
int d = rampDownPosition * channelCount + i;
|
||||
for (int t = 0; t < frameCount; t++) {
|
||||
out[o] = (short) ((rampDown[d] * (frameCount - t) + rampUp[u] * t) / frameCount);
|
||||
o += channelCount;
|
||||
d += channelCount;
|
||||
u += channelCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ public final class SonicAudioProcessor implements AudioProcessor {
|
||||
sonic.queueInput(shortBuffer);
|
||||
inputBuffer.position(inputBuffer.position() + inputSize);
|
||||
}
|
||||
int outputSize = sonic.getSamplesAvailable() * channelCount * 2;
|
||||
int outputSize = sonic.getFramesAvailable() * channelCount * 2;
|
||||
if (outputSize > 0) {
|
||||
if (buffer.capacity() < outputSize) {
|
||||
buffer = ByteBuffer.allocateDirect(outputSize).order(ByteOrder.nativeOrder());
|
||||
@ -227,7 +227,7 @@ public final class SonicAudioProcessor implements AudioProcessor {
|
||||
|
||||
@Override
|
||||
public boolean isEnded() {
|
||||
return inputEnded && (sonic == null || sonic.getSamplesAvailable() == 0);
|
||||
return inputEnded && (sonic == null || sonic.getFramesAvailable() == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user