Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View File

@ -0,0 +1,121 @@
package auratests;
import utest.Assert;
import kha.arrays.Float32Array;
import aura.Aura;
import aura.Types.Hertz;
import aura.types.AudioBuffer;
import aura.utils.BufferUtils;
import Utils;
class StaticValueGenerator extends aura.channels.generators.BaseGenerator {
public var counter = 0;
inline function new() {}
public static function create(): BaseChannelHandle {
return new BaseChannelHandle(new StaticValueGenerator());
}
function nextSamples(requestedSamples: AudioBuffer, sampleRate: Hertz) {
for (i in 0...requestedSamples.channelLength) {
for (c in 0...requestedSamples.numChannels) {
requestedSamples.getChannelView(c)[i] = (++counter) / 4096;
}
}
}
}
@:access(aura.Aura)
class TestAura extends utest.Test {
final staticInput = StaticValueGenerator.create();
function setup() {
staticInput.play();
@:privateAccess (cast staticInput.channel: StaticValueGenerator).counter = 0;
Aura.blockBufPos = 0;
}
function teardown() {
staticInput.setMixChannel(null);
}
function test_audioCallback_zeroIfNoInput() {
final compareArray = createEmptyF32Array(Aura.BLOCK_SIZE);
final buffer = new kha.audio2.Buffer(Aura.BLOCK_SIZE, 2, 44100);
fillBuffer(buffer.data, -1.0); // Poison buffer
Aura.audioCallback(new kha.internal.IntBox(Aura.BLOCK_SIZE), buffer);
assertEqualsFloat32Array(compareArray, buffer.data);
}
function test_audioCallback_zeroIfNoSampleCache() {
final compareArray = createEmptyF32Array(Aura.BLOCK_SIZE);
staticInput.setMixChannel(Aura.masterChannel);
// Force sampleCache to be null
Aura.p_samplesBuffer.set(null);
kha.audio2.Audio.disableGcInteractions = true;
final buffer = new kha.audio2.Buffer(Aura.BLOCK_SIZE, 2, 44100);
fillBuffer(buffer.data, -1.0); // Poison buffer
Aura.audioCallback(new kha.internal.IntBox(Aura.BLOCK_SIZE), buffer);
assertEqualsFloat32Array(compareArray, buffer.data);
}
function test_audioCallback_contiguouslyWritesBlocksToOutput() {
final numRequestedSamples = Aura.BLOCK_SIZE * 2 + 2;
final compareArray = new Float32Array(numRequestedSamples);
for (i in 0...compareArray.length) {
compareArray[i] = (i + 1) / 4096;
}
staticInput.setMixChannel(Aura.masterChannel);
final buffer = new kha.audio2.Buffer(numRequestedSamples, 2, 44100);
fillBuffer(buffer.data, -1.0); // Poison buffer
Aura.audioCallback(new kha.internal.IntBox(numRequestedSamples), buffer);
assertEqualsFloat32Array(compareArray, buffer.data);
}
function test_audioCallback_splitLargeBlockOverMultipleCallbacks() {
final numRequestedSamples = Std.int(Aura.BLOCK_SIZE / 2) - 2;
final compareArray = new Float32Array(3 * numRequestedSamples);
for (i in 0...compareArray.length) {
compareArray[i] = (i + 1) / 4096;
}
staticInput.setMixChannel(Aura.masterChannel);
final buffer = new kha.audio2.Buffer(numRequestedSamples, 2, 44100);
fillBuffer(buffer.data, -1.0); // Poison buffer
Aura.audioCallback(new kha.internal.IntBox(numRequestedSamples), buffer);
assertEqualsFloat32Array(compareArray.subarray(0, numRequestedSamples), buffer.data);
fillBuffer(buffer.data, -1.0); // Poison buffer
Aura.audioCallback(new kha.internal.IntBox(numRequestedSamples), buffer);
assertEqualsFloat32Array(compareArray.subarray(numRequestedSamples, numRequestedSamples * 2), buffer.data);
fillBuffer(buffer.data, -1.0); // Poison buffer
Aura.audioCallback(new kha.internal.IntBox(numRequestedSamples), buffer);
assertEqualsFloat32Array(compareArray.subarray(numRequestedSamples * 2, numRequestedSamples * 3), buffer.data);
}
// TODO
// function test_audioCallback_synchronizesMasterChannel() {}
// function test_audioCallback_updatesTime() {}
// function test_audioCallback_numChannelsOtherThanNUM_OUTPUT_CHANNELS() {
// TODO this needs changes in the audio callback. Too dynamic? But Kha might request this...
// }
}

View File

@ -0,0 +1,95 @@
package auratests;
import utest.Assert;
import aura.Aura;
import aura.Time;
import aura.Listener;
import aura.math.Vec3;
import Utils;
@:access(aura.Listener)
class TestListener extends utest.Test {
var listener: Listener;
function setup() {
listener = new Listener();
}
function teardown() {
Time.overrideTime = null;
}
function test_setLocation_multipleCallsOnFirstTimestep() {
Time.overrideTime = 0.0;
listener.setLocation(new Vec3(0.5, 0.6, 0.7));
Assert.floatEquals(0.5, listener.location.x);
Assert.floatEquals(0.6, listener.location.y);
Assert.floatEquals(0.7, listener.location.z);
Assert.floatEquals(0.0, listener.velocity.x);
Assert.floatEquals(0.0, listener.velocity.y);
Assert.floatEquals(0.0, listener.velocity.z);
Time.overrideTime = 0.0;
listener.setLocation(new Vec3(1.0, 2.0, 3.0));
Assert.floatEquals(1.0, listener.location.x);
Assert.floatEquals(2.0, listener.location.y);
Assert.floatEquals(3.0, listener.location.z);
Assert.floatEquals(0.0, listener.velocity.x);
Assert.floatEquals(0.0, listener.velocity.y);
Assert.floatEquals(0.0, listener.velocity.z);
}
function test_setLocation_firstCall_timeDeltaZero() {
Time.overrideTime = 0.0;
listener.setLocation(new Vec3(0.5, 0.6, 0.7));
Assert.floatEquals(0.5, listener.location.x);
Assert.floatEquals(0.6, listener.location.y);
Assert.floatEquals(0.7, listener.location.z);
Assert.floatEquals(0.0, listener.velocity.x);
Assert.floatEquals(0.0, listener.velocity.y);
Assert.floatEquals(0.0, listener.velocity.z);
}
function test_setLocation_firstCall_timeDeltaPositive() {
Time.overrideTime = 2.0;
listener.setLocation(new Vec3(0.5, 0.6, 0.7));
Assert.floatEquals(0.5, listener.location.x);
Assert.floatEquals(0.6, listener.location.y);
Assert.floatEquals(0.7, listener.location.z);
Assert.floatEquals(0.0, listener.velocity.x);
Assert.floatEquals(0.0, listener.velocity.y);
Assert.floatEquals(0.0, listener.velocity.z);
}
function test_setLocation_subsequentCalls_timeDeltaZero() {
// Regression test for https://github.com/MoritzBrueckner/aura/pull/8
Time.overrideTime = 1.0;
listener.setLocation(new Vec3(0.0, 0.0, 0.0));
Time.overrideTime = 3.0;
listener.setLocation(new Vec3(1.0, 2.0, 3.0));
Time.overrideTime = 3.0;
listener.setLocation(new Vec3(2.0, 4.0, 6.0));
Assert.floatEquals(2.0, listener.location.x);
Assert.floatEquals(4.0, listener.location.y);
Assert.floatEquals(6.0, listener.location.z);
// Compute velocity based on timestep 1.0
Assert.floatEquals(1.0, listener.velocity.x);
Assert.floatEquals(2.0, listener.velocity.y);
Assert.floatEquals(3.0, listener.velocity.z);
}
}

View File

@ -0,0 +1,95 @@
package auratests.channels;
import utest.Assert;
import aura.Aura;
import aura.channels.MixChannel;
import aura.channels.UncompBufferResamplingChannel;
class TestBaseChannelHandle extends utest.Test {
var handle: BaseChannelHandle;
var channel: UncompBufferResamplingChannel;
var data = new kha.arrays.Float32Array(8);
function setup() {
channel = new UncompBufferResamplingChannel(data, false, 44100);
handle = new BaseChannelHandle(channel);
}
function teardown() {}
function test_setMixChannelAddsInputIfNotYetExisting() {
final handle1 = new MixChannelHandle(new MixChannel());
final handle2 = new MixChannelHandle(new MixChannel());
Assert.equals(0, handle2.getNumInputs());
Assert.isTrue(handle1.setMixChannel(handle2));
Assert.equals(1, handle2.getNumInputs());
}
function test_setMixChannelDoesntAddAlreadyExistingInput() {
final handle1 = new MixChannelHandle(new MixChannel());
final handle2 = new MixChannelHandle(new MixChannel());
Assert.isTrue(handle1.setMixChannel(handle2));
Assert.equals(1, handle2.getNumInputs());
Assert.isTrue(handle1.setMixChannel(handle2));
Assert.equals(1, handle2.getNumInputs());
}
function test_setMixChannelNullRemovesInputIfExisting() {
final handle1 = new MixChannelHandle(new MixChannel());
final handle2 = new MixChannelHandle(new MixChannel());
Assert.equals(0, handle2.getNumInputs());
Assert.isTrue(handle1.setMixChannel(null));
Assert.equals(0, handle2.getNumInputs());
Assert.isTrue(handle1.setMixChannel(handle2));
Assert.equals(1, handle2.getNumInputs());
Assert.isTrue(handle1.setMixChannel(null));
Assert.equals(0, handle2.getNumInputs());
}
function test_setMixChannelSwitchingMixChannelCorrectlyChangesInputs() {
final handle1 = new MixChannelHandle(new MixChannel());
final handle2 = new MixChannelHandle(new MixChannel());
final handle3 = new MixChannelHandle(new MixChannel());
Assert.equals(0, handle2.getNumInputs());
Assert.equals(0, handle3.getNumInputs());
Assert.isTrue(handle1.setMixChannel(handle2));
Assert.equals(1, handle2.getNumInputs());
Assert.equals(0, handle3.getNumInputs());
Assert.isTrue(handle1.setMixChannel(handle3));
Assert.equals(0, handle2.getNumInputs());
Assert.equals(1, handle3.getNumInputs());
}
function test_setMixChannelSelfReferenceReturnsFalseAndRemovesInput() {
final handle1 = new MixChannelHandle(new MixChannel());
final handle2 = new MixChannelHandle(new MixChannel());
Assert.isTrue(handle1.setMixChannel(handle2));
Assert.equals(1, handle2.getNumInputs());
Assert.isFalse(handle1.setMixChannel(handle1));
Assert.equals(0, handle2.getNumInputs());
}
function test_setMixChannelCircularDependencyReturnsFalseAndRemovesInput() {
final handle1 = new MixChannelHandle(new MixChannel());
final handle2 = new MixChannelHandle(new MixChannel());
final handle3 = new MixChannelHandle(new MixChannel());
final handle4 = new MixChannelHandle(new MixChannel());
Assert.isTrue(handle3.setMixChannel(handle4));
Assert.isTrue(handle1.setMixChannel(handle2));
Assert.isTrue(handle2.setMixChannel(handle3));
Assert.isFalse(handle3.setMixChannel(handle1));
Assert.equals(0, handle4.getNumInputs());
}
}

View File

@ -0,0 +1,41 @@
package auratests.channels;
import utest.Assert;
import aura.channels.MixChannel;
@:access(aura.channels.MixChannel)
class TestMixChannel extends utest.Test {
var mixChannel: MixChannel;
var mixChannelHandle: MixChannelHandle;
function setupClass() {}
function setup() {
mixChannel = new MixChannel();
mixChannelHandle = new MixChannelHandle(mixChannel);
}
function teardown() {}
function test_startUnpausedAndUnfinished() {
// Regression test for https://github.com/MoritzBrueckner/aura/issues/7
final inputHandle = new MixChannelHandle(new MixChannel());
Assert.isFalse(mixChannel.paused);
Assert.isFalse(mixChannel.finished);
}
function test_isNotPlayable_ifNoInputChannelExists() {
Assert.isFalse(mixChannel.isPlayable());
}
function test_isPlayable_ifInputChannelExists() {
final inputHandle = new MixChannelHandle(new MixChannel());
inputHandle.setMixChannel(mixChannelHandle);
Assert.isTrue(mixChannel.isPlayable());
}
}

View File

@ -0,0 +1,218 @@
package auratests.channels;
import utest.Assert;
import kha.arrays.Float32Array;
import aura.Types.Balance;
import aura.channels.UncompBufferChannel;
import aura.dsp.sourcefx.SourceEffect;
import aura.types.AudioBuffer;
@:access(aura.channels.UncompBufferChannel)
class TestUncompBufferChannel extends utest.Test {
static inline var channelLength = 16;
var audioChannel: UncompBufferChannel;
var sourceFX1: SourceFXDummy;
var sourceFX2: SourceFXDummy;
final data = new Float32Array(2 * channelLength);
function setupClass() {}
function setup() {
audioChannel = new UncompBufferChannel(data, false);
sourceFX1 = new SourceFXDummy();
sourceFX2 = new SourceFXDummy();
audioChannel.addSourceEffect(sourceFX1);
audioChannel.addSourceEffect(sourceFX2);
}
function teardown() {}
function test_optionallyApplySourceEffects_isAppliedOnFirstPlay_ifNoEffectIsConfiguredToApplyOnReplay() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(false);
Assert.isFalse(sourceFX1.wasProcessCalled);
Assert.isFalse(sourceFX2.wasProcessCalled);
audioChannel.play(false);
Assert.isTrue(sourceFX1.wasProcessCalled);
Assert.isTrue(sourceFX2.wasProcessCalled);
}
function test_optionallyApplySourceEffects_isAppliedOnFirstPlay_ifAnyEffectIsConfiguredToApplyOnReplay() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(true);
Assert.isFalse(sourceFX1.wasProcessCalled);
Assert.isFalse(sourceFX2.wasProcessCalled);
audioChannel.play(false);
Assert.isTrue(sourceFX1.wasProcessCalled);
Assert.isTrue(sourceFX2.wasProcessCalled);
}
function test_optionallyApplySourceEffects_isNotAppliedOnSecondPlayAfterFinish_ifNoEffectIsConfiguredToApplyOnReplay() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(false);
audioChannel.play(false);
audioChannel.stop();
sourceFX1.wasProcessCalled = false;
sourceFX2.wasProcessCalled = false;
audioChannel.play(false);
Assert.isFalse(sourceFX1.wasProcessCalled);
Assert.isFalse(sourceFX2.wasProcessCalled);
}
function test_optionallyApplySourceEffects_isAppliedOnSecondPlayAfterFinish_ifAnyEffectIsConfiguredToApplyOnReplay() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(true);
audioChannel.play(false);
audioChannel.stop();
sourceFX1.wasProcessCalled = false;
sourceFX2.wasProcessCalled = false;
audioChannel.play(false);
Assert.isTrue(sourceFX1.wasProcessCalled);
Assert.isTrue(sourceFX2.wasProcessCalled);
}
function test_optionallyApplySourceEffects_isNotAppliedOnPlayAfterPause_ifNoEffectIsConfiguredToApplyOnReplay() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(false);
audioChannel.play(false);
audioChannel.pause();
sourceFX1.wasProcessCalled = false;
sourceFX2.wasProcessCalled = false;
audioChannel.play(false);
Assert.isFalse(sourceFX1.wasProcessCalled);
Assert.isFalse(sourceFX2.wasProcessCalled);
}
function test_optionallyApplySourceEffects_isNotAppliedOnPlayAfterPause_ifAnyEffectIsConfiguredToApplyOnReplay() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(true);
audioChannel.play(false);
audioChannel.pause();
sourceFX1.wasProcessCalled = false;
sourceFX2.wasProcessCalled = false;
audioChannel.play(false);
Assert.isFalse(sourceFX1.wasProcessCalled);
Assert.isFalse(sourceFX2.wasProcessCalled);
}
function test_optionallyApplySourceEffects_isNotAppliedOnRetrigger_ifNoEffectIsConfiguredToApplyOnReplay() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(false);
audioChannel.play(false);
sourceFX1.wasProcessCalled = false;
sourceFX2.wasProcessCalled = false;
audioChannel.play(true);
Assert.isFalse(sourceFX1.wasProcessCalled);
Assert.isFalse(sourceFX2.wasProcessCalled);
}
function test_optionallyApplySourceEffects_isAppliedOnRetrigger_ifAnyEffectIsConfiguredToApplyOnReplay() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(true);
audioChannel.play(false);
sourceFX1.wasProcessCalled = false;
sourceFX2.wasProcessCalled = false;
audioChannel.play(true);
Assert.isTrue(sourceFX1.wasProcessCalled);
Assert.isTrue(sourceFX2.wasProcessCalled);
}
function test_optionallyApplySourceEffects_isAppliedOnConsecutivePlays_ifEffectsHaveChanged() {
sourceFX1.applyOnReplay.store(false);
sourceFX2.applyOnReplay.store(false);
audioChannel.play(false);
audioChannel.stop();
sourceFX1.wasProcessCalled = false;
sourceFX2.wasProcessCalled = false;
final tempSourceFX = new SourceFXDummy();
audioChannel.addSourceEffect(tempSourceFX);
audioChannel.play(false);
Assert.isTrue(sourceFX1.wasProcessCalled);
Assert.isTrue(sourceFX2.wasProcessCalled);
audioChannel.stop();
sourceFX1.wasProcessCalled = false;
sourceFX2.wasProcessCalled = false;
audioChannel.removeSourceEffect(tempSourceFX);
audioChannel.play(false);
Assert.isTrue(sourceFX1.wasProcessCalled);
Assert.isTrue(sourceFX2.wasProcessCalled);
}
function test_nextSamples_onLoop_ApplySourceEffectsOnce() {
audioChannel.looping = true;
Assert.equals(0, sourceFX1.numProcessCalled);
Assert.equals(0, sourceFX2.numProcessCalled);
final outBuffer = new AudioBuffer(2, channelLength + 1);
audioChannel.nextSamples(outBuffer, 1000);
// Make sure process is only called once for _all_ channels
Assert.equals(1, sourceFX1.numProcessCalled);
Assert.equals(1, sourceFX2.numProcessCalled);
}
}
private class SourceFXDummy extends SourceEffect {
public var wasProcessCalled = false;
public var numProcessCalled = 0;
public function new() {}
function calculateRequiredChannelLength(srcChannelLength: Int): Int {
return srcChannelLength;
}
function process(srcBuffer: AudioBuffer, srcChannelLength: Int, dstBuffer: AudioBuffer): Int {
wasProcessCalled = true;
numProcessCalled++;
return srcChannelLength;
}
}

View File

@ -0,0 +1,126 @@
package auratests.channels;
import utest.Assert;
import kha.arrays.Float32Array;
import aura.Types.Balance;
import aura.channels.UncompBufferResamplingChannel;
import aura.dsp.sourcefx.SourceEffect;
import aura.types.AudioBuffer;
@:access(aura.channels.UncompBufferResamplingChannel)
class TestUncompBufferResamplingChannel extends utest.Test {
static inline var channelLength = 16;
var audioChannel: UncompBufferResamplingChannel;
final rampLeft = new Array<Float>();
final rampRight = new Array<Float>();
final data = new Float32Array(2 * channelLength); // interleaved stereo
function setupClass() {
rampLeft.resize(channelLength);
rampRight.resize(channelLength);
for (i in 0...channelLength) { // Fill data with a value ramp
final val = (i + 1) / channelLength;
data[i * 2 + 0] = rampLeft[i] = val;
data[i * 2 + 1] = rampRight[i] = -val;
}
}
function setup() {
audioChannel = new UncompBufferResamplingChannel(data, false, 1000);
}
function teardown() {}
function test_dataConversion() {
for (i in 0...channelLength) {
Assert.floatEquals(rampLeft[i], audioChannel.data.getChannelView(0)[i]);
Assert.floatEquals(rampRight[i], audioChannel.data.getChannelView(1)[i]);
}
}
function test_nextSamples() {
final outBuffer = new AudioBuffer(2, channelLength);
audioChannel.nextSamples(outBuffer, 1000);
for (i in 0...channelLength) {
Assert.floatEquals(rampLeft[i], outBuffer.getChannelView(0)[i]);
Assert.floatEquals(rampRight[i], outBuffer.getChannelView(1)[i]);
}
// Now the channel has processed all data and will reset position to 0
Assert.equals(0, audioChannel.playbackPosition);
Assert.floatEquals(0.0, audioChannel.floatPosition);
// No looping, but request more samples
final longOutBuffer = new AudioBuffer(2, channelLength + 4);
audioChannel.nextSamples(longOutBuffer, 1000);
for (i in 0...channelLength) {
Assert.floatEquals(rampLeft[i], longOutBuffer.getChannelView(0)[i]);
Assert.floatEquals(rampRight[i], longOutBuffer.getChannelView(1)[i]);
}
for (i in channelLength...channelLength + 4) {
Assert.floatEquals(0.0, longOutBuffer.getChannelView(0)[i]);
Assert.floatEquals(0.0, longOutBuffer.getChannelView(1)[i]);
}
// Now change the sample rate, second half should be zero
audioChannel.playbackPosition = 0;
audioChannel.floatPosition = 0.0;
audioChannel.nextSamples(outBuffer, 500);
for (i in Std.int(channelLength / 2)...channelLength) {
Assert.floatEquals(0.0, outBuffer.getChannelView(0)[i]);
Assert.floatEquals(0.0, outBuffer.getChannelView(1)[i]);
}
// Now with looping
audioChannel.playbackPosition = 0;
audioChannel.floatPosition = 0.0;
audioChannel.looping = true;
audioChannel.nextSamples(outBuffer, 500);
final halfChannelLength = Std.int(channelLength / 2);
for (i in 0...halfChannelLength) {
Assert.floatEquals(outBuffer.getChannelView(0)[i], outBuffer.getChannelView(0)[halfChannelLength + i], null, '$i');
Assert.floatEquals(outBuffer.getChannelView(1)[i], outBuffer.getChannelView(1)[halfChannelLength + i], null, '$i');
}
// TODO: check sample precise looping without gaps with unusual sample rates?
}
function test_nextSamples_onLoop_ApplySourceEffectsOnce() {
audioChannel.looping = true;
final sourceFX = new SourceFXDummy();
audioChannel.addSourceEffect(sourceFX);
Assert.equals(0, sourceFX.numProcessCalled);
final outBuffer = new AudioBuffer(2, channelLength + 1);
audioChannel.nextSamples(outBuffer, 1000);
// Make sure process is only called once for _all_ channels
Assert.equals(1, sourceFX.numProcessCalled);
}
}
private class SourceFXDummy extends SourceEffect {
public var numProcessCalled = 0;
public function new() {}
function calculateRequiredChannelLength(srcChannelLength: Int): Int {
return srcChannelLength;
}
function process(srcBuffer: AudioBuffer, srcChannelLength: Int, dstBuffer: AudioBuffer): Int {
numProcessCalled++;
return srcChannelLength;
}
}

View File

@ -0,0 +1,126 @@
package auratests.dsp;
import utest.Assert;
import kha.arrays.Float32Array;
import aura.Aura;
import aura.dsp.FFTConvolver;
import aura.types.AudioBuffer;
import aura.types.Complex;
import aura.utils.MathUtils;
import aura.utils.TestSignals;
@:access(aura.dsp.FFTConvolver)
class TestFFTConvolver extends utest.Test {
var audioBuffer: AudioBuffer;
var fftConvolver: FFTConvolver;
function setup() {
audioBuffer = new AudioBuffer(2, FFTConvolver.FFT_SIZE);
fftConvolver = new FFTConvolver();
}
function test_process_noFadeIfTemporalInterpLengthIsZero() {
fftConvolver.temporalInterpolationLength = 0;
for (i in 0...audioBuffer.channelLength) {
audioBuffer.getChannelView(0)[i] = Math.sin(i * 4 * Math.PI / audioBuffer.channelLength);
audioBuffer.getChannelView(1)[i] = Math.sin(i * 4 * Math.PI / audioBuffer.channelLength);
}
setImpulseFreqsToConstant(new Complex(1.0, 0.0));
fftConvolver.process(audioBuffer);
discardOverlapForNextProcess();
for (i in 0...FFTConvolver.FFT_SIZE) {
Assert.floatEquals(Math.sin(i * 4 * Math.PI / audioBuffer.channelLength), audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(Math.sin(i * 4 * Math.PI / audioBuffer.channelLength), audioBuffer.getChannelView(1)[i]);
}
setImpulseFreqsToConstant(new Complex(0.0, 0.0));
fftConvolver.process(audioBuffer);
for (i in 0...FFTConvolver.FFT_SIZE) {
Assert.floatEquals(0, audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(0, audioBuffer.getChannelView(1)[i]);
}
}
function test_process_crossfadeIfTemporalInterpLengthIsLargerZero() {
fftConvolver.temporalInterpolationLength = 20;
for (i in 0...audioBuffer.channelLength) {
audioBuffer.getChannelView(0)[i] = Math.sin(i * 4 * Math.PI / audioBuffer.channelLength);
audioBuffer.getChannelView(1)[i] = Math.sin(i * 4 * Math.PI / audioBuffer.channelLength);
}
setImpulseFreqsToConstant(new Complex(1.0, 0.0));
fftConvolver.process(audioBuffer);
discardOverlapForNextProcess();
for (i in 0...FFTConvolver.FFT_SIZE) {
final t = minF(i, fftConvolver.temporalInterpolationLength) / fftConvolver.temporalInterpolationLength;
Assert.floatEquals(lerp(0.0, Math.sin(i * 4 * Math.PI / audioBuffer.channelLength), t), audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(lerp(0.0, Math.sin(i * 4 * Math.PI / audioBuffer.channelLength), t), audioBuffer.getChannelView(1)[i]);
}
for (i in 0...audioBuffer.channelLength) {
audioBuffer.getChannelView(0)[i] = Math.sin(i * 8 * Math.PI / audioBuffer.channelLength);
audioBuffer.getChannelView(1)[i] = Math.sin(i * 8 * Math.PI / audioBuffer.channelLength);
}
setImpulseFreqsToConstant(new Complex(0.0, 0.0));
fftConvolver.process(audioBuffer);
for (i in 0...FFTConvolver.FFT_SIZE) {
final t = minF(i, fftConvolver.temporalInterpolationLength) / fftConvolver.temporalInterpolationLength;
Assert.floatEquals(lerp(Math.sin(i * 8 * Math.PI / audioBuffer.channelLength), 0.0, t), audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(lerp(Math.sin(i * 8 * Math.PI / audioBuffer.channelLength), 0.0, t), audioBuffer.getChannelView(1)[i]);
}
}
function test_process_crossfadeEntireChunkSize() {
fftConvolver.temporalInterpolationLength = -1;
for (i in 0...audioBuffer.channelLength) {
audioBuffer.getChannelView(0)[i] = Math.sin(i * 4 * Math.PI / audioBuffer.channelLength);
audioBuffer.getChannelView(1)[i] = Math.sin(i * 4 * Math.PI / audioBuffer.channelLength);
}
setImpulseFreqsToConstant(new Complex(1.0, 0.0));
fftConvolver.process(audioBuffer);
discardOverlapForNextProcess();
for (i in 0...FFTConvolver.FFT_SIZE) {
final t = minF(i, FFTConvolver.CHUNK_SIZE) / FFTConvolver.CHUNK_SIZE;
Assert.floatEquals(lerp(0.0, Math.sin(i * 4 * Math.PI / audioBuffer.channelLength), t), audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(lerp(0.0, Math.sin(i * 4 * Math.PI / audioBuffer.channelLength), t), audioBuffer.getChannelView(1)[i]);
}
for (i in 0...audioBuffer.channelLength) {
audioBuffer.getChannelView(0)[i] = Math.sin(i * 8 * Math.PI / audioBuffer.channelLength);
audioBuffer.getChannelView(1)[i] = Math.sin(i * 8 * Math.PI / audioBuffer.channelLength);
}
setImpulseFreqsToConstant(new Complex(0.0, 0.0));
fftConvolver.process(audioBuffer);
for (i in 0...FFTConvolver.FFT_SIZE) {
final t = minF(i, FFTConvolver.CHUNK_SIZE) / FFTConvolver.CHUNK_SIZE;
Assert.floatEquals(lerp(Math.sin(i * 8 * Math.PI / audioBuffer.channelLength), 0.0, t), audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(lerp(Math.sin(i * 8 * Math.PI / audioBuffer.channelLength), 0.0, t), audioBuffer.getChannelView(1)[i]);
}
}
function setImpulseFreqsToConstant(value: Complex) {
for (i in 0...FFTConvolver.FFT_SIZE) {
fftConvolver.impulseFFT.getOutput(0 + fftConvolver.currentImpulseAlternationIndex)[i] = value;
fftConvolver.impulseFFT.getOutput(2 + fftConvolver.currentImpulseAlternationIndex)[i] = value;
}
fftConvolver.currentImpulseAlternationIndex = 1 - fftConvolver.currentImpulseAlternationIndex;
fftConvolver.overlapLength[0] = FFTConvolver.CHUNK_SIZE;
fftConvolver.overlapLength[1] = FFTConvolver.CHUNK_SIZE;
fftConvolver.prevImpulseLengths[0] = FFTConvolver.CHUNK_SIZE;
fftConvolver.prevImpulseLengths[1] = FFTConvolver.CHUNK_SIZE;
}
function discardOverlapForNextProcess() {
for (c in 0...FFTConvolver.NUM_CHANNELS) {
for (i in 0...fftConvolver.overlapPrev[c].length) {
fftConvolver.overlapPrev[c][i] = 0.0;
}
}
}
}

View File

@ -0,0 +1,67 @@
package auratests.dsp;
import utest.Assert;
import aura.Aura;
import aura.dsp.FractionalDelayLine;
import aura.types.AudioBuffer;
import aura.utils.TestSignals;
@:access(aura.dsp.FractionalDelayLine)
class TestFractionalDelayLine extends utest.Test {
var audioBuffer: AudioBuffer;
var delayLine: FractionalDelayLine;
function setup() {
audioBuffer = new AudioBuffer(2, 8);
delayLine = new FractionalDelayLine(2, 8);
}
function test_zeroDelayTime_noDelay() {
TestSignals.fillUnitImpulse(audioBuffer.getChannelView(0));
TestSignals.fillUnitImpulse(audioBuffer.getChannelView(1));
delayLine.at_setDelayLength(Left, 0.0);
delayLine.at_setDelayLength(Right, 0.0);
delayLine.process(audioBuffer);
Assert.floatEquals(1.0, audioBuffer.getChannelView(0)[0]);
Assert.floatEquals(0.0, audioBuffer.getChannelView(0)[1]);
Assert.floatEquals(1.0, audioBuffer.getChannelView(1)[0]);
Assert.floatEquals(0.0, audioBuffer.getChannelView(1)[1]);
}
function test_integralDelayTime_independentChannels() {
TestSignals.fillUnitImpulse(audioBuffer.getChannelView(0));
TestSignals.fillUnitImpulse(audioBuffer.getChannelView(1));
delayLine.at_setDelayLength(Left, 1.0);
delayLine.at_setDelayLength(Right, 3.0);
delayLine.process(audioBuffer);
Assert.floatEquals(0.0, audioBuffer.getChannelView(0)[0]);
Assert.floatEquals(1.0, audioBuffer.getChannelView(0)[1]);
Assert.floatEquals(0.0, audioBuffer.getChannelView(1)[0]);
Assert.floatEquals(1.0, audioBuffer.getChannelView(1)[3]);
}
function test_floatDelayTime_independentChannels() {
TestSignals.fillUnitImpulse(audioBuffer.getChannelView(0));
TestSignals.fillUnitImpulse(audioBuffer.getChannelView(1));
delayLine.at_setDelayLength(Left, 0.8);
delayLine.at_setDelayLength(Right, 3.4);
delayLine.process(audioBuffer);
Assert.floatEquals(0.2, audioBuffer.getChannelView(0)[0]);
Assert.floatEquals(0.8, audioBuffer.getChannelView(0)[1]);
Assert.floatEquals(0.6, audioBuffer.getChannelView(1)[3]);
Assert.floatEquals(0.4, audioBuffer.getChannelView(1)[4]);
}
}

View File

@ -0,0 +1,102 @@
package auratests.dsp;
import utest.Assert;
import kha.arrays.Float32Array;
import aura.Aura;
import aura.dsp.SparseConvolver;
import aura.types.AudioBuffer;
import aura.utils.TestSignals;
@:access(aura.dsp.SparseConvolver)
class TestSparseConvolver extends utest.Test {
var audioBuffer: AudioBuffer;
var sparseConvolver: SparseConvolver;
function setup() {
audioBuffer = new AudioBuffer(2, 512);
sparseConvolver = new SparseConvolver(1, 4);
}
function test_simpleDelay() {
for (i in 0...audioBuffer.channelLength) {
audioBuffer.getChannelView(0)[i] = Math.sin(i * 2 * Math.PI / audioBuffer.channelLength);
audioBuffer.getChannelView(1)[i] = Math.cos(i * 2 * Math.PI / audioBuffer.channelLength);
}
final impulse = sparseConvolver.impulseBuffer;
impulse.setImpulsePos(0, 3);
impulse.setImpulseMagnitude(0, 1.0);
sparseConvolver.process(audioBuffer);
final wanted = new AudioBuffer(2, audioBuffer.channelLength);
for (i in 0...wanted.channelLength) {
wanted.getChannelView(0)[i] = Math.sin((i - 3) * 2 * Math.PI / wanted.channelLength);
wanted.getChannelView(1)[i] = Math.cos((i - 3) * 2 * Math.PI / wanted.channelLength);
}
for (i in 0...3) {
Assert.floatEquals(0, audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(0, audioBuffer.getChannelView(1)[i]);
}
for (i in 3...wanted.channelLength) {
Assert.floatEquals(wanted.getChannelView(0)[i], audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(wanted.getChannelView(1)[i], audioBuffer.getChannelView(1)[i]);
}
// Overlap
audioBuffer.clear();
sparseConvolver.process(audioBuffer);
for (i in 0...3) {
Assert.floatEquals(wanted.getChannelView(0)[i], audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(wanted.getChannelView(1)[i], audioBuffer.getChannelView(1)[i]);
}
for (i in 3...wanted.channelLength) {
Assert.floatEquals(0, audioBuffer.getChannelView(0)[i]);
Assert.floatEquals(0, audioBuffer.getChannelView(1)[i]);
}
}
}
@:access(aura.dsp.SparseConvolver.SparseImpulseBuffer)
class TestSparseImpulseBuffer extends utest.Test {
var buffer: SparseImpulseBuffer;
function setup() {
buffer = new SparseImpulseBuffer(4);
}
function test_length() {
Assert.equals(1, new SparseImpulseBuffer(1).length);
Assert.equals(2, new SparseImpulseBuffer(2).length);
Assert.equals(3, new SparseImpulseBuffer(3).length);
Assert.equals(1024, new SparseImpulseBuffer(1024).length);
}
function test_impulsePos_notOverwrittenByOtherImpulses() {
buffer.setImpulsePos(0, 3);
buffer.setImpulsePos(1, 9);
buffer.setImpulsePos(2, 17);
buffer.setImpulsePos(3, 42);
Assert.equals(3, buffer.getImpulsePos(0));
Assert.equals(9, buffer.getImpulsePos(1));
Assert.equals(17, buffer.getImpulsePos(2));
Assert.equals(42, buffer.getImpulsePos(3));
}
function test_impulseMagnitude_notOverwrittenByOtherImpulses() {
buffer.setImpulseMagnitude(0, 0.3);
buffer.setImpulseMagnitude(1, 0.9);
buffer.setImpulseMagnitude(2, 0.17);
buffer.setImpulseMagnitude(3, 0.42);
Assert.floatEquals(0.3, buffer.getImpulseMagnitude(0));
Assert.floatEquals(0.9, buffer.getImpulseMagnitude(1));
Assert.floatEquals(0.17, buffer.getImpulseMagnitude(2));
Assert.floatEquals(0.42, buffer.getImpulseMagnitude(3));
}
}

View File

@ -0,0 +1,283 @@
package auratests.dsp.panner;
import utest.Assert;
import aura.Aura;
import aura.Time;
import aura.dsp.panner.Panner;
import aura.math.Vec3;
import aura.types.AudioBuffer;
import Utils;
private class NonAbstractPanner extends Panner {
public function process(buffer: AudioBuffer) {}
}
@:access(aura.channels.BaseChannel)
@:access(aura.channels.BaseChannelHandle)
@:access(aura.dsp.panner.Panner)
class TestPanner extends utest.Test {
var handle: BaseChannelHandle;
var panner: Panner;
function setup() {
handle = Utils.createDummyHandle();
panner = new NonAbstractPanner(handle);
}
function teardown() {
Time.overrideTime = null;
}
function test_setLocation_multipleCallsOnFirstTimestep() {
Time.overrideTime = 0.0;
panner.setLocation(new Vec3(0.5, 0.6, 0.7));
Assert.floatEquals(0.5, panner.location.x);
Assert.floatEquals(0.6, panner.location.y);
Assert.floatEquals(0.7, panner.location.z);
Assert.floatEquals(0.0, panner.velocity.x);
Assert.floatEquals(0.0, panner.velocity.y);
Assert.floatEquals(0.0, panner.velocity.z);
Time.overrideTime = 0.0;
panner.setLocation(new Vec3(1.0, 2.0, 3.0));
Assert.floatEquals(1.0, panner.location.x);
Assert.floatEquals(2.0, panner.location.y);
Assert.floatEquals(3.0, panner.location.z);
Assert.floatEquals(0.0, panner.velocity.x);
Assert.floatEquals(0.0, panner.velocity.y);
Assert.floatEquals(0.0, panner.velocity.z);
}
function test_setLocation_firstCall_timeDeltaZero() {
Time.overrideTime = 0.0;
panner.setLocation(new Vec3(0.5, 0.6, 0.7));
Assert.floatEquals(0.5, panner.location.x);
Assert.floatEquals(0.6, panner.location.y);
Assert.floatEquals(0.7, panner.location.z);
Assert.floatEquals(0.0, panner.velocity.x);
Assert.floatEquals(0.0, panner.velocity.y);
Assert.floatEquals(0.0, panner.velocity.z);
}
function test_setLocation_firstCall_timeDeltaPositive() {
Time.overrideTime = 2.0;
panner.setLocation(new Vec3(0.5, 0.6, 0.7));
Assert.floatEquals(0.5, panner.location.x);
Assert.floatEquals(0.6, panner.location.y);
Assert.floatEquals(0.7, panner.location.z);
Assert.floatEquals(0.0, panner.velocity.x);
Assert.floatEquals(0.0, panner.velocity.y);
Assert.floatEquals(0.0, panner.velocity.z);
}
function test_setLocation_subsequentCalls_timeDeltaZero() {
// Regression test for https://github.com/MoritzBrueckner/aura/pull/8
Time.overrideTime = 1.0;
panner.setLocation(new Vec3(0.0, 0.0, 0.0));
Time.overrideTime = 3.0;
panner.setLocation(new Vec3(1.0, 2.0, 3.0));
Time.overrideTime = 3.0;
panner.setLocation(new Vec3(2.0, 4.0, 6.0));
Assert.floatEquals(2.0, panner.location.x);
Assert.floatEquals(4.0, panner.location.y);
Assert.floatEquals(6.0, panner.location.z);
// Compute velocity based on timestep 1.0
Assert.floatEquals(1.0, panner.velocity.x);
Assert.floatEquals(2.0, panner.velocity.y);
Assert.floatEquals(3.0, panner.velocity.z);
}
function test_update3D_noDopplerJumpIfLocationWasUninitialized() {
Time.overrideTime = 0.0;
panner.setLocation(new Vec3(20.0, 0.0, 0.0));
panner.update3D();
handle.channel.synchronize();
Assert.floatEquals(1.0, handle.channel.pDopplerRatio.targetValue);
}
function test_noDopplerEffect_ifNoMovement() {
Time.overrideTime = 0.0;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(5.0, 4.0, 3.0));
panner.update3D();
Time.overrideTime = 0.5;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(5.0, 4.0, 3.0));
panner.update3D();
handle.channel.synchronize();
Assert.floatEquals(1.0, handle.channel.pDopplerRatio.targetValue);
}
function test_calculateDoppler_physicallyCorrectValues_pannerMovesAway() {
Time.overrideTime = 0.0;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.update3D();
Time.overrideTime = 0.5;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(2.0, 0.0, 0.0));
panner.update3D();
Assert.floatEquals(4.0, @:privateAccess panner.velocity.length);
Assert.floatEquals(0.0, @:privateAccess aura.Aura.listener.velocity.length);
handle.channel.synchronize();
// Values calculated at
// https://www.omnicalculator.com/physics/doppler-effect?c=EUR&v=f0:5000!Hz,v:343.4!ms,vs:2!ms,vr:0!ms
// Assuming that their implementation is correct
Assert.floatEquals(4942.43 / 5000, handle.channel.pDopplerRatio.targetValue);
}
function test_calculateDoppler_physicallyCorrectValues_listenerMovesAway() {
Time.overrideTime = 0.0;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.update3D();
Time.overrideTime = 0.5;
aura.Aura.listener.setLocation(new Vec3(2.0, 0.0, 0.0));
panner.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.update3D();
Assert.floatEquals(4.0, @:privateAccess aura.Aura.listener.velocity.length);
Assert.floatEquals(0.0, @:privateAccess panner.velocity.length);
handle.channel.synchronize();
Assert.floatEquals(4941.76 / 5000, handle.channel.pDopplerRatio.targetValue);
}
function test_calculateDoppler_physicallyCorrectValues_pannerMovesCloser() {
Time.overrideTime = 0.0;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(4.0, 0.0, 0.0));
panner.update3D();
Time.overrideTime = 0.5;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(2.0, 0.0, 0.0));
panner.update3D();
Assert.floatEquals(4.0, @:privateAccess panner.velocity.length);
Assert.floatEquals(0.0, @:privateAccess aura.Aura.listener.velocity.length);
handle.channel.synchronize();
Assert.floatEquals(5058.93 / 5000, handle.channel.pDopplerRatio.targetValue);
}
function test_calculateDoppler_physicallyCorrectValues_listenerMovesCloser() {
Time.overrideTime = 0.0;
aura.Aura.listener.setLocation(new Vec3(4.0, 0.0, 0.0));
panner.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.update3D();
Time.overrideTime = 0.5;
aura.Aura.listener.setLocation(new Vec3(2.0, 0.0, 0.0));
panner.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.update3D();
Assert.floatEquals(4.0, @:privateAccess aura.Aura.listener.velocity.length);
Assert.floatEquals(0.0, @:privateAccess panner.velocity.length);
handle.channel.synchronize();
Assert.floatEquals(5058.24 / 5000, handle.channel.pDopplerRatio.targetValue);
}
function test_calculateDoppler_noDopplerEffectIfNoRadialVelocity() {
Time.overrideTime = 0.0;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(2.0, 0.0, 0.0));
panner.update3D();
Time.overrideTime = 0.5;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.update3D();
handle.channel.synchronize();
Assert.floatEquals(1, handle.channel.pDopplerRatio.targetValue);
}
function test_calculateDoppler_noDopplerEffectIfNoRadialVelocity2() {
Time.overrideTime = 0.0;
aura.Aura.listener.setLocation(new Vec3(5.0, 0.0, 0.0));
panner.setLocation(new Vec3(5.0, 0.0, 0.0));
panner.update3D();
Time.overrideTime = 0.5;
aura.Aura.listener.setLocation(new Vec3(5.0, 0.0, 0.0));
panner.setLocation(new Vec3(5.0, 0.0, 0.0));
panner.update3D();
handle.channel.synchronize();
Assert.floatEquals(1, handle.channel.pDopplerRatio.targetValue);
}
function test_calculateDoppler_noDopplerEffectIfNoRadialVelocity3() {
Time.overrideTime = 0.0;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(2.0, 2.0, 0.0));
panner.update3D();
Time.overrideTime = 0.5;
aura.Aura.listener.setLocation(new Vec3(0.0, 0.0, 0.0));
panner.setLocation(new Vec3(0.0, 2.0, 0.0));
panner.update3D();
handle.channel.synchronize();
Assert.floatEquals(1, handle.channel.pDopplerRatio.targetValue);
}
function test_dopplerEffect_isZeroIfPannerMovesCloserAtSpeedOfSound() {
Time.overrideTime = 0.0;
panner.setLocation(new Vec3(Panner.SPEED_OF_SOUND + 1, 0.0, 0.0));
panner.update3D();
Time.overrideTime = 1.0;
panner.setLocation(new Vec3(1, 0.0, 0.0));
panner.update3D();
handle.channel.synchronize();
Assert.floatEquals(0, handle.channel.pDopplerRatio.targetValue);
}
function test_dopplerEffect_pannerMovesCloserAboveSpeedOfSound() {
Time.overrideTime = 0.0;
panner.setLocation(new Vec3(Panner.SPEED_OF_SOUND + 5, 0.0, 0.0));
panner.update3D();
Time.overrideTime = 1.0;
panner.setLocation(new Vec3(1, 0.0, 0.0));
panner.update3D();
handle.channel.synchronize();
Assert.floatEquals(-85.85, handle.channel.pDopplerRatio.targetValue);
}
}

View File

@ -0,0 +1,66 @@
package auratests.format;
import haxe.Int64;
import haxe.io.Bytes;
import haxe.io.BytesInput;
import utest.Assert;
using aura.format.InputExtension;
class TestInputExtension extends utest.Test {
var bytes: Bytes;
var inp: BytesInput;
// 10000000 01000000 00100000 00010000 - 00001000 00000100 00000010 00000001
var inputValue = Int64.make(
1 << 31 | 1 << 22 | 1 << 13 | 1 << 4,
1 << 27 | 1 << 18 | 1 << 9 | 1
);
// 00000001 00000010 00000100 00001000 - 00010000 00100000 01000000 10000000
var inputValueInverted = Int64.make(
1 << 24 | 1 << 17 | 1 << 10 | 1 << 3,
1 << 28 | 1 << 21 | 1 << 14 | 1 << 7
);
function setup() {
bytes = Bytes.alloc(8);
inp = new BytesInput(bytes);
}
function test_readInt64_littleEndian_correctRead() {
bytes.setInt64(0, inputValue); // setInt64 is little-endian
inp.bigEndian = false;
assertI64Equals(inputValue, inp.readInt64());
}
function test_readInt64_bigEndian_correctRead() {
bytes.setInt64(0, inputValue);
inp.bigEndian = true;
assertI64Equals(inputValueInverted, inp.readInt64());
}
function test_readUint32_isUnsigned() {
bytes.setInt32(0, 1 << 31);
inp.bigEndian = false;
assertI64Equals(Int64.make(0, -2147483648/* -2^31, sign bit doesn't mean anything in low part */) , inp.readUInt32());
}
function test_readUint32_littleEndian_correctRead() {
bytes.setInt32(0, inputValue.high);
inp.bigEndian = false;
assertI64Equals(Int64.make(0, inputValue.high), inp.readUInt32());
}
function test_readUint32_bigEndian_correctRead() {
bytes.setInt32(0, inputValue.high);
inp.bigEndian = true;
assertI64Equals(Int64.make(0, inputValueInverted.low), inp.readUInt32());
}
function assertI64Equals(want: Int64, have: Int64, ?pos: haxe.PosInfos) {
final errorMessage = 'Expected (high: ${want.high}, low: ${want.low}), have (high: ${have.high}, low: ${have.low}).';
Assert.isTrue(want.low == have.low && want.high == have.high, errorMessage, pos);
}
}

View File

@ -0,0 +1,92 @@
package auratests.math;
import utest.Assert;
import aura.math.FFT;
import Utils;
@:depends(auratests.types.TestComplexArray)
class TestFFT extends utest.Test {
function test_bitReverseUint32() {
// Haxe has some issue with signed/unsigned ints here, so we instead
// compare the individual strings as bits. This also makes the output in
// case of assertion failures much nicer to look at.
Assert.equals(Utils.int32ToBytesString(0xFF000000), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x000000FF, 32)));
Assert.equals(Utils.int32ToBytesString(0x00FF0000), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x0000FF00, 32)));
Assert.equals(Utils.int32ToBytesString(0x0000FF00), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x00FF0000, 32)));
Assert.equals(Utils.int32ToBytesString(0x000000FF), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0xFF000000, 32)));
Assert.equals(Utils.int32ToBytesString(0xC0000000), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x00000003, 32)));
Assert.equals(Utils.int32ToBytesString(0x20000000), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x00000004, 32)));
Assert.equals(Utils.int32ToBytesString(0x00FF0000), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x000000FF, 24)));
Assert.equals(Utils.int32ToBytesString(0x0000FF00), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x0000FF00, 24)));
Assert.equals(Utils.int32ToBytesString(0x000000FF), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x00FF0000, 24)));
Assert.equals(Utils.int32ToBytesString(0x0000FF00), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x000000FF, 16)));
Assert.equals(Utils.int32ToBytesString(0x00000003), Utils.int32ToBytesString(@:privateAccess aura.math.FFT.bitReverseUint32(0x00000018, 5)));
}
function test_RealValuedFFT() {
final realFFT = new RealValuedFFT(64, 2, 1);
final inputBuffer = realFFT.getInput(0);
for (i in 0...realFFT.size) {
inputBuffer[i] = Math.sin(i / realFFT.size * 2 * Math.PI * 8);
}
realFFT.forwardFFT(0, 0);
var maxIdx = 0;
var maxVal = realFFT.getOutput(0)[0].real;
for (i in 1...realFFT.size) {
final val = realFFT.getOutput(0)[i].real;
if (val > maxVal) {
maxVal = val;
maxIdx = i;
}
}
Assert.equals(8, maxIdx);
realFFT.inverseFFT(1, 0);
// Assert that ifft(fft(array)) == array
for (i in 0...realFFT.size) {
Assert.floatEquals(realFFT.getInput(0)[i], realFFT.getInput(1)[i]);
}
}
function test_ComplexValuedFFT() {
final cplxFFT = new ComplexValuedFFT(64, 2, 1);
final inputBuffer = cplxFFT.getInput(0);
for (i in 0...cplxFFT.size) {
inputBuffer[i].real = Math.sin(i / cplxFFT.size * 2 * Math.PI * 8);
inputBuffer[i].imag = 0.0;
}
cplxFFT.forwardFFT(0, 0);
// var maxIdx = 0;
// var maxVal = cplxFFT.getOutput(0)[0].real;
// for (i in 1...cplxFFT.size) {
// final val = cplxFFT.getOutput(0)[i].real;
// if (val > maxVal) {
// maxVal = val;
// maxIdx = i;
// }
// }
// Assert.equals(8, maxIdx);
cplxFFT.inverseFFT(1, 0);
// Assert that ifft(fft(array)) == array
for (i in 0...cplxFFT.size) {
Assert.floatEquals(cplxFFT.getInput(0)[i].real, cplxFFT.getInput(1)[i].real);
Assert.floatEquals(cplxFFT.getInput(0)[i].imag, cplxFFT.getInput(1)[i].imag);
}
}
}

View File

@ -0,0 +1,34 @@
package auratests.threading;
import aura.threading.Fifo;
import utest.Assert;
class TestFifo extends utest.Test {
var fifo: Fifo<Int>;
function setup() {
fifo = new Fifo();
}
function test_popFromEmptyFifoReturnsNull() {
Assert.isNull(fifo.tryPop());
}
function test_fifoIsEmptyAfterPoppingLastItem() {
fifo.add(0);
fifo.add(1);
fifo.tryPop();
fifo.tryPop();
Assert.isNull(fifo.tryPop());
}
function test_ItemsArePoppedInOrderTheyAreAdded() {
fifo.add(0);
fifo.add(1);
Assert.equals(0, fifo.tryPop());
Assert.equals(1, fifo.tryPop());
}
}

View File

@ -0,0 +1,323 @@
package auratests.types;
import utest.Assert;
import kha.arrays.Float32Array;
import aura.types.AudioBuffer;
import aura.utils.BufferUtils;
import Utils;
class TestAudioBuffer extends utest.Test {
var buffer: AudioBuffer;
function setup() {
buffer = new AudioBuffer(2, 8);
}
function teardown() {}
function test_numChannels() {
Assert.equals(2, buffer.numChannels);
assertRaisesAssertion(() -> {
new AudioBuffer(0, 8);
});
assertRaisesAssertion(() -> {
new AudioBuffer(-1, 8);
});
}
function test_channelLength() {
Assert.equals(8, buffer.channelLength);
assertRaisesAssertion(() -> {
new AudioBuffer(2, 0);
});
assertRaisesAssertion(() -> {
new AudioBuffer(2, -1);
});
}
function test_AudioBufferChannelView_GetSetWithSameIndexAccessSameValue() {
final view0 = buffer.getChannelView(0);
final view1 = buffer.getChannelView(1);
view0[1] = 8.0;
view1[6] = 4.0;
Assert.floatEquals(8.0, view0[1]);
Assert.floatEquals(4.0, view1[6]);
}
function test_channelViewsDoNotOverlap() {
final view0 = buffer.getChannelView(0);
final view1 = buffer.getChannelView(1);
// Fill views one after each other
for (i in 0...buffer.numChannels) {
view0[i] = 0.0;
}
for (i in 0...buffer.numChannels) {
view1[i] = 1.0;
}
for (i in 0...buffer.numChannels) {
Assert.floatEquals(0.0, view0[i]);
}
}
function test_interleaveToFloat32Array_assertionIfCopyingNegativeAmountOfSamples() {
final array = new Float32Array(4);
assertRaisesAssertion(() -> {
buffer.interleaveToFloat32Array(array, 0, 0, -1);
});
}
function test_interleaveToFloat32Array_assertionIfArrayIsTooSmall() {
final array = new Float32Array(4);
assertRaisesAssertion(() -> {
buffer.interleaveToFloat32Array(array, 0, 0, buffer.channelLength);
});
}
function test_interleaveToFloat32Array_assertionIfSourceOffsetIsNegative() {
final array = new Float32Array(64);
assertRaisesAssertion(() -> {
buffer.interleaveToFloat32Array(array, -1, 0, buffer.channelLength);
});
}
function test_interleaveToFloat32Array_assertionIfTargetOffsetIsNegative() {
final array = new Float32Array(16);
assertRaisesAssertion(() -> {
buffer.interleaveToFloat32Array(array, 0, -1, buffer.channelLength);
});
}
function test_interleaveToFloat32Array_assertionIfSourceOffsetIsTooLarge() {
final array = new Float32Array(64);
assertRaisesAssertion(() -> {
buffer.interleaveToFloat32Array(array, 500, 0, buffer.channelLength);
});
}
function test_interleaveToFloat32Array_assertionIfTargetOffsetIsTooLarge() {
final array = new Float32Array(16);
assertRaisesAssertion(() -> {
buffer.interleaveToFloat32Array(array, 0, 500, buffer.channelLength);
});
}
function test_clear() {
fillBuffer(buffer.getChannelView(0), 1.0);
fillBuffer(buffer.getChannelView(1), -1.0);
buffer.clear();
final zeroArray = new Float32Array(buffer.numChannels * buffer.channelLength);
clearBuffer(zeroArray);
assertEqualsFloat32Array(zeroArray, buffer.rawData);
}
function test_interleaveToFloat32Array_numSamplesToCopy() {
final view0 = buffer.getChannelView(0);
final view1 = buffer.getChannelView(1);
// Fill views one after each other
for (i in 0...buffer.channelLength) {
view0[i] = i;
}
for (i in 0...buffer.channelLength) {
view1[i] = 8 + i;
}
final targetArray = new Float32Array(32);
clearBuffer(targetArray);
final compareArray = new Float32Array(32);
clearBuffer(compareArray);
compareArray[0] = 0;
compareArray[1] = 8;
compareArray[2] = 1;
compareArray[3] = 9;
compareArray[4] = 2;
compareArray[5] = 10;
compareArray[6] = 3;
compareArray[7] = 11;
compareArray[8] = 4;
compareArray[9] = 12;
buffer.interleaveToFloat32Array(targetArray, 0, 0, 5);
assertEqualsFloat32Array(compareArray, targetArray);
}
function test_interleaveToFloat32Array_sourceOffset() {
final view0 = buffer.getChannelView(0);
final view1 = buffer.getChannelView(1);
// Fill views one after each other
for (i in 0...buffer.channelLength) {
view0[i] = i;
}
for (i in 0...buffer.channelLength) {
view1[i] = 8 + i;
}
final targetArray = new Float32Array(32);
clearBuffer(targetArray);
final compareArray = new Float32Array(32);
clearBuffer(compareArray);
compareArray[0] = 2;
compareArray[1] = 10;
compareArray[2] = 3;
compareArray[3] = 11;
compareArray[4] = 4;
compareArray[5] = 12;
compareArray[6] = 5;
compareArray[7] = 13;
compareArray[8] = 6;
compareArray[9] = 14;
compareArray[10] = 7;
compareArray[11] = 15;
buffer.interleaveToFloat32Array(targetArray, 2, 0, 6);
assertEqualsFloat32Array(compareArray, targetArray);
}
function test_interleaveToFloat32Array_targetOffset() {
final view0 = buffer.getChannelView(0);
final view1 = buffer.getChannelView(1);
// Fill views one after each other
for (i in 0...buffer.channelLength) {
view0[i] = i;
}
for (i in 0...buffer.channelLength) {
view1[i] = 8 + i;
}
final targetArray = new Float32Array(32);
clearBuffer(targetArray);
final compareArray = new Float32Array(32);
clearBuffer(compareArray);
compareArray[10] = 0;
compareArray[11] = 8;
compareArray[12] = 1;
compareArray[13] = 9;
compareArray[14] = 2;
compareArray[15] = 10;
compareArray[16] = 3;
compareArray[17] = 11;
compareArray[18] = 4;
compareArray[19] = 12;
compareArray[20] = 5;
compareArray[21] = 13;
compareArray[22] = 6;
compareArray[23] = 14;
compareArray[24] = 7;
compareArray[25] = 15;
buffer.interleaveToFloat32Array(targetArray, 0, 10, 8);
assertEqualsFloat32Array(compareArray, targetArray);
}
function test_test_deinterleaveFromFloat32Array_assertionIfSourceArrayTooSmall() {
final array = new Float32Array(15);
assertRaisesAssertion(() -> {
buffer.deinterleaveFromFloat32Array(array, 2);
});
}
function test_test_deinterleaveFromFloat32Array_assertionIfNumSourceChannelsNegative() {
final array = new Float32Array(16);
assertRaisesAssertion(() -> {
buffer.deinterleaveFromFloat32Array(array, -1);
});
}
function test_test_deinterleaveFromFloat32Array_assertionIfMoreSourceChannelsThanBufferChannels() {
final array = new Float32Array(32);
assertRaisesAssertion(() -> {
buffer.deinterleaveFromFloat32Array(array, 3);
});
}
function test_deinterleaveFromFloat32Array_oneChannelOnly() {
buffer.clear();
final sourceArray = new Float32Array(8);
for (i in 0...sourceArray.length) {
sourceArray[i] = 2 + i;
}
final compareArrayLeft = new Float32Array(8);
compareArrayLeft[0] = 2;
compareArrayLeft[1] = 3;
compareArrayLeft[2] = 4;
compareArrayLeft[3] = 5;
compareArrayLeft[4] = 6;
compareArrayLeft[5] = 7;
compareArrayLeft[6] = 8;
compareArrayLeft[7] = 9;
final compareArrayRight = new Float32Array(8);
clearBuffer(compareArrayRight);
buffer.deinterleaveFromFloat32Array(sourceArray, 1);
assertEqualsFloat32Array(compareArrayLeft, buffer.getChannelView(0));
assertEqualsFloat32Array(compareArrayRight, buffer.getChannelView(1));
}
function test_deinterleaveFromFloat32Array_allChannels() {
buffer.clear();
final sourceArray = new Float32Array(16);
for (i in 0...8) {
sourceArray[2 * i] = 2 + i;
sourceArray[2 * i + 1] = -1 - i;
}
final compareArrayLeft = new Float32Array(8);
compareArrayLeft[0] = 2;
compareArrayLeft[1] = 3;
compareArrayLeft[2] = 4;
compareArrayLeft[3] = 5;
compareArrayLeft[4] = 6;
compareArrayLeft[5] = 7;
compareArrayLeft[6] = 8;
compareArrayLeft[7] = 9;
final compareArrayRight = new Float32Array(8);
compareArrayRight[0] = -1;
compareArrayRight[1] = -2;
compareArrayRight[2] = -3;
compareArrayRight[3] = -4;
compareArrayRight[4] = -5;
compareArrayRight[5] = -6;
compareArrayRight[6] = -7;
compareArrayRight[7] = -8;
buffer.deinterleaveFromFloat32Array(sourceArray, 2);
assertEqualsFloat32Array(compareArrayLeft, buffer.getChannelView(0));
assertEqualsFloat32Array(compareArrayRight, buffer.getChannelView(1));
}
}

View File

@ -0,0 +1,50 @@
package auratests.types;
import utest.Assert;
import aura.types.Complex;
import aura.types.ComplexArray;
class TestComplexArray extends utest.Test {
var array: ComplexArray;
function setup() {
array = new ComplexArray(4);
}
function teardown() {}
function test_length() {
Assert.equals(4, array.length);
}
@:depends(test_length)
function test_isZeroInitialized() {
for (i in 0...array.length) {
var tmp = array[i];
Assert.equals(0.0, tmp.real);
Assert.equals(0.0, tmp.imag);
}
}
function test_getSetBasicFunctionality() {
array[0] = new Complex(3.14, 1.1);
Assert.floatEquals(3.14, array[0].real);
Assert.floatEquals(1.1, array[0].imag);
}
function test_getSetCorrectStride() {
array[0] = new Complex(3.14, 1.1);
array[1] = new Complex(9.9, 1.23);
Assert.floatEquals(9.9, array[1].real);
Assert.floatEquals(1.23, array[1].imag);
Assert.floatEquals(3.14, array[0].real); // Ensure no overrides happen
Assert.floatEquals(1.1, array[0].imag);
}
function test_getSetArrayCopiesValues() {
var tmp = new Complex(3.14, 1.1);
array[0] = tmp;
Assert.isTrue(tmp != array[0]);
}
}

View File

@ -0,0 +1,42 @@
package auratests.utils;
import utest.Assert;
import kha.arrays.Float32Array;
import aura.utils.BufferUtils;
import aura.utils.CircularBuffer;
import Utils;
@:access(aura.utils.CircularBuffer)
class TestCircularBuffer extends utest.Test {
var buffer: CircularBuffer;
function setup() {
buffer = new CircularBuffer(8);
}
function teardown() {}
function test_new_assertThatSizeIsPositiveNumber() {
assertRaisesAssertion(() -> {
new CircularBuffer(0);
});
assertRaisesAssertion(() -> {
new CircularBuffer(-1);
});
}
function test_new_dataInitializedToZero() {
// Please note that this test always succeeds on JS and has additional
// false negatives on other targets, there the test still succeeds if data
// is not actively initialized but the values are still 0
// TODO If Aura has it's own array types at some point in time, implement
// active poisoning of values if unit tests are run
final compareArray = createEmptyF32Array(buffer.length);
assertEqualsFloat32Array(compareArray, buffer.data);
}
}

View File

@ -0,0 +1,110 @@
package auratests.utils;
import kha.simd.Float32x4;
import utest.Assert;
import aura.types.AudioBuffer.AudioBufferChannelView;
import aura.utils.Interpolator.LinearInterpolator;
class TestLinearInterpolator extends utest.Test {
static inline var NUM_SAMPLES = 8;
function test_isInitializedToTargetValue() {
final interp = new LinearInterpolator(0.0);
Assert.floatEquals(0.0, interp.currentValue);
Assert.floatEquals(0.0, interp.lastValue);
Assert.floatEquals(0.0, interp.targetValue);
}
function test_stepSizeIsCorrectForPositiveSteps() {
final interp = new LinearInterpolator(0.0);
interp.targetValue = 4.0;
final stepSize = interp.getLerpStepSize(NUM_SAMPLES);
Assert.floatEquals(0.5, stepSize);
}
function test_stepSizeIsCorrectForNegativeSteps() {
final interp = new LinearInterpolator(0.0);
interp.targetValue = -4.0;
final stepSize = interp.getLerpStepSize(NUM_SAMPLES);
Assert.floatEquals(-0.5, stepSize);
}
function test_stepsReachTargetValue() {
final interp = new LinearInterpolator(0.0);
interp.targetValue = 4.0;
final stepSize = interp.getLerpStepSize(NUM_SAMPLES);
for (_ in 0...NUM_SAMPLES) {
interp.currentValue += stepSize;
}
Assert.floatEquals(interp.targetValue, interp.currentValue);
}
function test_updateLastUpdatesLastAndCurrentValue() {
final interp = new LinearInterpolator(0.0);
interp.targetValue = 4.0;
interp.updateLast();
Assert.floatEquals(interp.targetValue, interp.lastValue);
Assert.floatEquals(interp.targetValue, interp.currentValue);
}
function test_getLerpStepSizes32x4IsCorrectForPositiveSteps() {
final interp = new LinearInterpolator(0.0);
interp.targetValue = 4.0;
final stepSizes = interp.getLerpStepSizes32x4(NUM_SAMPLES);
Assert.floatEquals(0.5, Float32x4.getFast(stepSizes, 0));
Assert.floatEquals(1.0, Float32x4.getFast(stepSizes, 1));
Assert.floatEquals(1.5, Float32x4.getFast(stepSizes, 2));
Assert.floatEquals(2.0, Float32x4.getFast(stepSizes, 3));
}
function test_getLerpStepSizes32x4IsCorrectForNegativeSteps() {
final interp = new LinearInterpolator(0.0);
interp.targetValue = -4.0;
final stepSizes = interp.getLerpStepSizes32x4(NUM_SAMPLES);
Assert.floatEquals(-0.5, Float32x4.getFast(stepSizes, 0));
Assert.floatEquals(-1.0, Float32x4.getFast(stepSizes, 1));
Assert.floatEquals(-1.5, Float32x4.getFast(stepSizes, 2));
Assert.floatEquals(-2.0, Float32x4.getFast(stepSizes, 3));
}
function test_applySIMD32x4() {
final samples = new AudioBufferChannelView(NUM_SAMPLES);
for (i in 0...NUM_SAMPLES) {
samples[i] = 1.0;
}
final interp = new LinearInterpolator(0.0);
interp.targetValue = 4.0;
final stepSizes = interp.getLerpStepSizes32x4(NUM_SAMPLES);
interp.applySIMD32x4(samples, 0, stepSizes);
Assert.floatEquals(0.5, samples[0]);
Assert.floatEquals(1.0, samples[1]);
Assert.floatEquals(1.5, samples[2]);
Assert.floatEquals(2.0, samples[3]);
Assert.floatEquals(2.0, interp.currentValue);
interp.applySIMD32x4(samples, 4, stepSizes);
Assert.floatEquals(2.5, samples[4]);
Assert.floatEquals(3.0, samples[5]);
Assert.floatEquals(3.5, samples[6]);
Assert.floatEquals(4.0, samples[7]);
Assert.floatEquals(4.0, interp.currentValue);
}
}

View File

@ -0,0 +1,26 @@
package auratests.utils;
import utest.Assert;
import aura.utils.MathUtils;
class TestMathUtils extends utest.Test {
function test_maxMin() {
Assert.equals(18, minI(42, 18));
Assert.equals(18, minI(18, 42));
Assert.equals(-99, minI(-99, 42));
Assert.equals(42, maxI(42, 18));
Assert.equals(42, maxI(18, 42));
Assert.equals(42, maxI(-99, 42));
Assert.equals(3.14, minF(3.14, 11.11));
Assert.equals(3.14, minF(11.11, 3.14));
Assert.equals(-28.1, minF(-28.1, 3.07));
Assert.equals(11.11, maxF(3.14, 11.11));
Assert.equals(11.11, maxF(11.11, 3.14));
Assert.equals(3.07, maxF(-28.1, 3.07));
}
}

View File

@ -0,0 +1,82 @@
package auratests.utils;
import utest.Assert;
import kha.arrays.Float32Array;
import aura.utils.Resampler;
import Utils;
class TestResampler extends utest.Test {
final sourceData = new Float32Array(4);
final sourceSampleRate = 100;
function setupClass() {
sourceData[0] = 0.0;
sourceData[1] = 1.0;
sourceData[2] = 2.0;
sourceData[3] = 3.0;
}
function test_getResampleLength() {
Assert.equals(Std.int(sourceData.length / 2), Resampler.getResampleLength(sourceData.length, sourceSampleRate, Std.int(sourceSampleRate / 2)));
Assert.equals(sourceData.length * 2, Resampler.getResampleLength(sourceData.length, sourceSampleRate, sourceSampleRate * 2));
}
function test_sourceSamplePosToTargetPos() {
Assert.floatEquals(1.0, Resampler.sourceSamplePosToTargetPos(2.0, 100, 50));
Assert.floatEquals(4.0, Resampler.sourceSamplePosToTargetPos(2.0, 100, 200));
}
function test_targetSamplePosToSourcePos() {
Assert.floatEquals(4.0, Resampler.targetSamplePosToSourcePos(2.0, 100, 50));
Assert.floatEquals(1.0, Resampler.targetSamplePosToSourcePos(2.0, 100, 200));
}
function test_sampleAtTargetPositionLerp_SamplesExactValuesAtDiscretePositions() {
Assert.floatEquals(0.0, Resampler.sampleAtTargetPositionLerp(sourceData, 0.0, sourceSampleRate, 100));
Assert.floatEquals(1.0, Resampler.sampleAtTargetPositionLerp(sourceData, 1.0, sourceSampleRate, 100));
}
function test_sampleAtTargetPositionLerp_InterpolatesLinearlyBetweenDiscreteSamples() {
Assert.floatEquals(0.5, Resampler.sampleAtTargetPositionLerp(sourceData, 0.5, sourceSampleRate, 100));
Assert.floatEquals(0.3, Resampler.sampleAtTargetPositionLerp(sourceData, 0.3, sourceSampleRate, 100));
}
function test_sampleAtTargetPositionLerp_AssertsSamplePositionNotNegative() {
assertRaisesAssertion(() -> {
Resampler.sampleAtTargetPositionLerp(sourceData, -1.0, sourceSampleRate, 100);
});
}
function test_sampleAtTargetPositionLerp_ClampsValuesOutOfUpperDataBounds() {
Assert.floatEquals(3.0, Resampler.sampleAtTargetPositionLerp(sourceData, 3.5, sourceSampleRate, 100));
Assert.floatEquals(3.0, Resampler.sampleAtTargetPositionLerp(sourceData, 4.0, sourceSampleRate, 100));
}
function test_sampleAtTargetPositionLerp_DifferentSampleRatesUsed() {
Assert.floatEquals(0.0, Resampler.sampleAtTargetPositionLerp(sourceData, 0.0, sourceSampleRate, Std.int(sourceSampleRate / 2)));
Assert.floatEquals(1.0, Resampler.sampleAtTargetPositionLerp(sourceData, 0.5, sourceSampleRate, Std.int(sourceSampleRate / 2)));
Assert.floatEquals(2.0, Resampler.sampleAtTargetPositionLerp(sourceData, 1.0, sourceSampleRate, Std.int(sourceSampleRate / 2)));
Assert.floatEquals(0.0, Resampler.sampleAtTargetPositionLerp(sourceData, 0.0, sourceSampleRate, sourceSampleRate * 2));
Assert.floatEquals(0.25, Resampler.sampleAtTargetPositionLerp(sourceData, 0.5, sourceSampleRate, sourceSampleRate * 2));
Assert.floatEquals(0.5, Resampler.sampleAtTargetPositionLerp(sourceData, 1.0, sourceSampleRate, sourceSampleRate * 2));
}
function test_resampleFloat32Array() {
final targetData = new Float32Array(8);
Resampler.resampleFloat32Array(sourceData, 100, targetData, 200);
Assert.floatEquals(0.0, targetData[0]);
Assert.floatEquals(0.5, targetData[1]);
Assert.floatEquals(1.0, targetData[2]);
Assert.floatEquals(1.5, targetData[3]);
Assert.floatEquals(2.0, targetData[4]);
Assert.floatEquals(2.5, targetData[5]);
Assert.floatEquals(3.0, targetData[6]);
Assert.floatEquals(3.0, targetData[7]); // Don't extrapolate data
}
}