forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
121
lib/aura/Tests/auratests/TestAura.hx
Normal file
121
lib/aura/Tests/auratests/TestAura.hx
Normal 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...
|
||||
// }
|
||||
}
|
95
lib/aura/Tests/auratests/TestListener.hx
Normal file
95
lib/aura/Tests/auratests/TestListener.hx
Normal 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);
|
||||
}
|
||||
}
|
95
lib/aura/Tests/auratests/channels/TestBaseChannelHandle.hx
Normal file
95
lib/aura/Tests/auratests/channels/TestBaseChannelHandle.hx
Normal 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());
|
||||
}
|
||||
}
|
41
lib/aura/Tests/auratests/channels/TestMixChannel.hx
Normal file
41
lib/aura/Tests/auratests/channels/TestMixChannel.hx
Normal 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());
|
||||
}
|
||||
}
|
218
lib/aura/Tests/auratests/channels/TestUncompBufferChannel.hx
Normal file
218
lib/aura/Tests/auratests/channels/TestUncompBufferChannel.hx
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
126
lib/aura/Tests/auratests/dsp/TestFFTConvolver.hx
Normal file
126
lib/aura/Tests/auratests/dsp/TestFFTConvolver.hx
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
67
lib/aura/Tests/auratests/dsp/TestFractionalDelayLine.hx
Normal file
67
lib/aura/Tests/auratests/dsp/TestFractionalDelayLine.hx
Normal 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]);
|
||||
}
|
||||
}
|
102
lib/aura/Tests/auratests/dsp/TestSparseConvolver.hx
Normal file
102
lib/aura/Tests/auratests/dsp/TestSparseConvolver.hx
Normal 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));
|
||||
}
|
||||
}
|
283
lib/aura/Tests/auratests/dsp/panner/TestPanner.hx
Normal file
283
lib/aura/Tests/auratests/dsp/panner/TestPanner.hx
Normal 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);
|
||||
}
|
||||
}
|
66
lib/aura/Tests/auratests/format/TestInputExtension.hx
Normal file
66
lib/aura/Tests/auratests/format/TestInputExtension.hx
Normal 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);
|
||||
}
|
||||
}
|
92
lib/aura/Tests/auratests/math/TestFFT.hx
Normal file
92
lib/aura/Tests/auratests/math/TestFFT.hx
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
34
lib/aura/Tests/auratests/threading/TestFifo.hx
Normal file
34
lib/aura/Tests/auratests/threading/TestFifo.hx
Normal 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());
|
||||
}
|
||||
}
|
323
lib/aura/Tests/auratests/types/TestAudioBuffer.hx
Normal file
323
lib/aura/Tests/auratests/types/TestAudioBuffer.hx
Normal 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));
|
||||
}
|
||||
}
|
50
lib/aura/Tests/auratests/types/TestComplexArray.hx
Normal file
50
lib/aura/Tests/auratests/types/TestComplexArray.hx
Normal 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]);
|
||||
}
|
||||
}
|
42
lib/aura/Tests/auratests/utils/TestCircularBuffer.hx
Normal file
42
lib/aura/Tests/auratests/utils/TestCircularBuffer.hx
Normal 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);
|
||||
}
|
||||
}
|
110
lib/aura/Tests/auratests/utils/TestLinearInterpolator.hx
Normal file
110
lib/aura/Tests/auratests/utils/TestLinearInterpolator.hx
Normal 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);
|
||||
}
|
||||
}
|
26
lib/aura/Tests/auratests/utils/TestMathUtils.hx
Normal file
26
lib/aura/Tests/auratests/utils/TestMathUtils.hx
Normal 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));
|
||||
}
|
||||
}
|
82
lib/aura/Tests/auratests/utils/TestResampler.hx
Normal file
82
lib/aura/Tests/auratests/utils/TestResampler.hx
Normal 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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user