mirror of
https://github.com/samsonjs/media.git
synced 2026-04-04 11:05:47 +00:00
Extract method for calculating expected accumulated truncation error
This is prework for implementing `RandomParameterizedSpeedChangingAudioProcessorTest`, which depends on Sonic's resampling algorithm's behaviour. This is a non-functional refactor. PiperOrigin-RevId: 686874593
This commit is contained in:
parent
2a1e71b203
commit
b78395b325
2 changed files with 71 additions and 17 deletions
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package androidx.media3.common.audio;
|
||||
|
||||
import static androidx.media3.common.audio.SonicTestingUtils.calculateAccumulatedTruncationErrorForResampling;
|
||||
import static androidx.media3.test.utils.TestUtil.generateFloatInRange;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.lang.Math.max;
|
||||
|
|
@ -163,31 +164,18 @@ public final class RandomParameterizedSonicTest {
|
|||
}
|
||||
sonic.flush();
|
||||
|
||||
BigDecimal bigSampleRate = new BigDecimal(SAMPLE_RATE);
|
||||
BigDecimal bigLength = new BigDecimal(String.valueOf(streamLength));
|
||||
// The scale of expectedSize will be bigLength.scale() - speed.scale(). Thus, the result should
|
||||
// always yield an integer.
|
||||
BigDecimal expectedSize = bigLength.divide(speed, RoundingMode.HALF_EVEN);
|
||||
|
||||
// Calculate number of times that Sonic accumulates truncation error. Set scale to 20 decimal
|
||||
// places, so that division doesn't return an integral.
|
||||
BigDecimal errorCount =
|
||||
bigLength.divide(bigSampleRate, /* scale= */ 20, RoundingMode.HALF_EVEN);
|
||||
|
||||
// Calculate what truncation error Sonic is accumulating, calculated as:
|
||||
// inputSampleRate / speed - (int) inputSampleRate / speed. Set scale to 20 decimal places, so
|
||||
// that division doesn't return an integral.
|
||||
BigDecimal individualError =
|
||||
bigSampleRate.divide(speed, /* scale */ 20, RoundingMode.HALF_EVEN);
|
||||
individualError =
|
||||
individualError.subtract(individualError.setScale(/* newScale= */ 0, RoundingMode.FLOOR));
|
||||
// Calculate total accumulated error = (int) floor(errorCount * individualError).
|
||||
BigDecimal accumulatedError =
|
||||
errorCount.multiply(individualError).setScale(/* newScale= */ 0, RoundingMode.FLOOR);
|
||||
long accumulatedTruncationError =
|
||||
calculateAccumulatedTruncationErrorForResampling(
|
||||
bigLength, new BigDecimal(SAMPLE_RATE), speed);
|
||||
|
||||
assertThat(readSampleCount)
|
||||
.isWithin(1)
|
||||
.of(expectedSize.longValueExact() - accumulatedError.longValueExact());
|
||||
.of(expectedSize.longValueExact() - accumulatedTruncationError);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2024 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.common.audio;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/** Testing utils class related to {@link Sonic} */
|
||||
/* package */ final class SonicTestingUtils {
|
||||
/**
|
||||
* Returns expected accumulated truncation error for {@link Sonic}'s resampling algorithm, given
|
||||
* an input length, input sample rate, and resampling rate.
|
||||
*
|
||||
* <p><b>Note:</b> This method is only necessary until we address b/361768785 and fix the
|
||||
* underlying truncation issue.
|
||||
*
|
||||
* <p>The accumulated truncation error is calculated as follows:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Individual truncation error: Divide sample rate by resampling rate, and calculate delta
|
||||
* between floating point result and truncated int representation.
|
||||
* <li>Truncation accumulation count: Divide length by sample rate to obtain number of times
|
||||
* that truncation error accumulates.
|
||||
* <li>Accumulated truncation error: Multiply results of 1 and 2.
|
||||
* </ol>
|
||||
*
|
||||
* @param length Length of input in frames.
|
||||
* @param sampleRate Input sample rate of {@link Sonic} instance.
|
||||
* @param resamplingRate Resampling rate given by {@code pitch * (inputSampleRate /
|
||||
* outputSampleRate)}.
|
||||
*/
|
||||
public static long calculateAccumulatedTruncationErrorForResampling(
|
||||
BigDecimal length, BigDecimal sampleRate, BigDecimal resamplingRate) {
|
||||
// Calculate number of times that Sonic accumulates truncation error. Set scale to 20 decimal
|
||||
// places, so that division doesn't return an integer.
|
||||
BigDecimal errorCount = length.divide(sampleRate, /* scale= */ 20, RoundingMode.HALF_EVEN);
|
||||
|
||||
// Calculate what truncation error Sonic is accumulating, calculated as:
|
||||
// inputSampleRate / resamplingRate - (int) inputSampleRate / resamplingRate. Set scale to 20
|
||||
// decimal places, so that division doesn't return an integer.
|
||||
BigDecimal individualError =
|
||||
sampleRate.divide(resamplingRate, /* scale */ 20, RoundingMode.HALF_EVEN);
|
||||
individualError =
|
||||
individualError.subtract(individualError.setScale(/* newScale= */ 0, RoundingMode.FLOOR));
|
||||
// Calculate total accumulated error = (int) floor(errorCount * individualError).
|
||||
BigDecimal accumulatedError =
|
||||
errorCount.multiply(individualError).setScale(/* newScale= */ 0, RoundingMode.FLOOR);
|
||||
|
||||
return accumulatedError.longValueExact();
|
||||
}
|
||||
|
||||
private SonicTestingUtils() {}
|
||||
}
|
||||
Loading…
Reference in a new issue