forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
80
lib/aura/Tests/.vscode/tasks.json
vendored
Normal file
80
lib/aura/Tests/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Aura: Run unit tests",
|
||||
"type": "process",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
},
|
||||
|
||||
"command": "node",
|
||||
"args": [
|
||||
"run.js"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {
|
||||
"KHA_PATH": "${command:kha.findKha}",
|
||||
"ELECTRON_BIN": "${command:kha.findKhaElectron}",
|
||||
|
||||
// "ELECTRON_NO_ATTACH_CONSOLE": "true",
|
||||
}
|
||||
},
|
||||
|
||||
"problemMatcher": [
|
||||
"$haxe", // Default Haxe matcher
|
||||
{
|
||||
// Electron
|
||||
"owner": "custom",
|
||||
"fileLocation": ["relative", "${workspaceFolder}"],
|
||||
"pattern": [
|
||||
// {
|
||||
// "regexp": "^\\s*Error:\\s+(.*)\\s*$",
|
||||
// "message": 1
|
||||
// },
|
||||
// {
|
||||
// "regexp": "^\\s*Stack:\\s*$",
|
||||
// },
|
||||
// {
|
||||
// "regexp": "^\\s*$",
|
||||
// },
|
||||
// {
|
||||
// "regexp": "^\\s*Called from\\s+(.*)\\s+\\(file:\\/\\/\\/(.*)\\s+line\\s+(\\d+)\\s+column\\s+(\\d+)\\)\\s*$",
|
||||
// "code": 1,
|
||||
// "file": 2,
|
||||
// "line": 3,
|
||||
// "column": 4
|
||||
// }
|
||||
{
|
||||
"regexp": "^\\s*Error:\\s+Uncaught\\s+(.*):(\\d+):\\s+(.*)\\s*$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"message": 3
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
// Also catch exceptions thrown by test failures in nodejs
|
||||
"owner": "haxe",
|
||||
"fileLocation": ["relative", "${workspaceFolder}"],
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^\\<ref\\s+\\*1\\>\\s+Error:\\s+(.*):(\\d+):(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"message": 3,
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
18
lib/aura/Tests/Data/electron-append.js
Normal file
18
lib/aura/Tests/Data/electron-append.js
Normal file
@ -0,0 +1,18 @@
|
||||
electron.ipcMain.on('log-main', (event, type, text) => {
|
||||
switch (type) {
|
||||
case "log":
|
||||
console.log(text);
|
||||
break;
|
||||
case "info":
|
||||
console.info(text);
|
||||
break;
|
||||
case "warn":
|
||||
console.warn(text);
|
||||
break;
|
||||
case "error":
|
||||
console.error(text);
|
||||
break;
|
||||
default:
|
||||
throw "Unreachable";
|
||||
}
|
||||
});
|
20
lib/aura/Tests/Data/index.html
Normal file
20
lib/aura/Tests/Data/index.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!--
|
||||
Custom index.html to prevent a wrong layout of test results due to the Kha canvas.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Aura Unit Tests</title>
|
||||
</head>
|
||||
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<!--
|
||||
We still need to include the canvas to prevent Kha errors, but it's
|
||||
at size (0, 0).
|
||||
-->
|
||||
<canvas id="khanvas" width="0" height="0" tabindex="-1"></canvas>
|
||||
|
||||
<script src="kha.js"></script>
|
||||
</body>
|
||||
</html>
|
96
lib/aura/Tests/Main.hx
Normal file
96
lib/aura/Tests/Main.hx
Normal file
@ -0,0 +1,96 @@
|
||||
package;
|
||||
|
||||
import utest.Runner;
|
||||
import utest.ui.Report;
|
||||
|
||||
#if instrument
|
||||
import instrument.Instrumentation;
|
||||
#end
|
||||
|
||||
class Main {
|
||||
static function main() {
|
||||
|
||||
kha.System.start({title: "Aura Unit Tests", width: 1024, height: 768}, (window: kha.Window) -> {
|
||||
|
||||
replaceConsoleFunctions();
|
||||
|
||||
#if (AURA_ASSERT_LEVEL!="Debug")
|
||||
trace("Warning: Running tests below highest assertion level, some tests might erroneously succeed");
|
||||
#end
|
||||
|
||||
kha.Assets.loadEverything(() -> {
|
||||
kha.audio2.Audio.samplesPerSecond = 44100;
|
||||
aura.Aura.init();
|
||||
|
||||
var runner = new Runner();
|
||||
runner.addCases(auratests, true);
|
||||
|
||||
// addCases() only allows one class per file (https://github.com/haxe-utest/utest/blob/f759c0aa257aa723b3dd607cf7cb53d16194d13f/src/utest/Runner.hx#L171),
|
||||
// so we manually add classes here where this is not the case
|
||||
runner.addCase(new auratests.dsp.TestSparseConvolver.TestSparseImpulseBuffer());
|
||||
|
||||
runner.onComplete.add((_) -> {
|
||||
#if instrument
|
||||
Instrumentation.endInstrumentation(Coverage);
|
||||
#end
|
||||
});
|
||||
|
||||
Report.create(runner);
|
||||
// new utest.ui.text.PrintReport(runner);
|
||||
runner.run();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
In Kha applications, `console.log()` calls called by `trace` are called
|
||||
from within the renderer process which prevents them from showing up in
|
||||
the console (instead they only show up in the devtools console).
|
||||
|
||||
A possible workaround is to run electron with `--enable-logging`,
|
||||
but this will show the traces in a bunch of irrelevant and noisy debug
|
||||
information and on Windows a bunch of terminal windows are opened if
|
||||
electron is not directly called from the shell. So instead, we send
|
||||
traces to the main thread and then log them there.
|
||||
|
||||
**See:**
|
||||
- Log in main process/renderer process:
|
||||
- https://stackoverflow.com/a/31759944/9985959
|
||||
|
||||
- Overriding console functions:
|
||||
- https://stackoverflow.com/a/30197398/9985959
|
||||
|
||||
- Electron opening multiple empty terminals on Windows:
|
||||
- https://github.com/electron/electron/issues/3846
|
||||
- https://github.com/electron/electron/issues/4582
|
||||
- https://github.com/electron-userland/spectron/issues/60#issuecomment-482070086
|
||||
**/
|
||||
static function replaceConsoleFunctions() {
|
||||
#if kha_debug_html5
|
||||
final oldConsole: Dynamic = js.Syntax.code("window.console");
|
||||
|
||||
function log(text: Dynamic) {
|
||||
oldConsole.log(text);
|
||||
js.Syntax.code("window.electron.logToMainProcess('log', {0})", text);
|
||||
}
|
||||
|
||||
function info(text: Dynamic) {
|
||||
oldConsole.info(text);
|
||||
js.Syntax.code("window.electron.logToMainProcess('info', {0})", text);
|
||||
}
|
||||
|
||||
function warn(text: Dynamic) {
|
||||
oldConsole.warn(text);
|
||||
js.Syntax.code("window.electron.logToMainProcess('warn', {0})", text);
|
||||
}
|
||||
|
||||
function error(text: Dynamic) {
|
||||
oldConsole.error(text);
|
||||
js.Syntax.code("window.electron.logToMainProcess('error', {0})", text);
|
||||
}
|
||||
|
||||
js.Syntax.code("window.console = {log: {0}, info: {1}, warn: {2}, error: {3}}", log, info, warn, error);
|
||||
#end
|
||||
}
|
||||
}
|
2
lib/aura/Tests/TestDeps.hxml
Normal file
2
lib/aura/Tests/TestDeps.hxml
Normal file
@ -0,0 +1,2 @@
|
||||
-lib utest:1.13.2
|
||||
-lib instrument:git:https://github.com/AlexHaxe/haxe-instrument.git#92a5691c7e77a696532e2e13ac1f717841f43015
|
48
lib/aura/Tests/Utils.hx
Normal file
48
lib/aura/Tests/Utils.hx
Normal file
@ -0,0 +1,48 @@
|
||||
package;
|
||||
|
||||
import haxe.PosInfos;
|
||||
|
||||
import utest.Assert;
|
||||
|
||||
import kha.arrays.Float32Array;
|
||||
|
||||
import aura.Aura;
|
||||
import aura.channels.UncompBufferChannel;
|
||||
|
||||
inline function createDummyHandle(): BaseChannelHandle {
|
||||
final data = new kha.arrays.Float32Array(8);
|
||||
final channel = new UncompBufferChannel(data, false);
|
||||
return new BaseChannelHandle(channel);
|
||||
}
|
||||
|
||||
inline function int32ToBytesString(i: Int): String {
|
||||
var str = "";
|
||||
for (j in 0...32) {
|
||||
final mask = 1 << (31 - j);
|
||||
str += (i & mask) == 0 ? "0" : "1";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
inline function assertRaisesAssertion(func: Void->Void) {
|
||||
#if (AURA_ASSERT_LEVEL!="NoAssertions")
|
||||
Assert.raises(func, aura.utils.Assert.AuraAssertionException);
|
||||
#else
|
||||
Assert.pass();
|
||||
#end
|
||||
}
|
||||
|
||||
function assertEqualsFloat32Array(expected: Float32Array, have: Float32Array, ?pos: PosInfos) {
|
||||
if (expected.length != have.length) {
|
||||
Assert.fail('Expected Float32Array of length ${expected.length}, but got length ${have.length}', pos);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i in 0...expected.length) {
|
||||
if (!@:privateAccess Assert._floatEquals(expected[i], have[i])) {
|
||||
Assert.fail('Expected value at index $i to be ${expected[i]}, but got ${have[i]} (only first difference reported)', pos);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Assert.pass(null, pos);
|
||||
}
|
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
|
||||
}
|
||||
}
|
7
lib/aura/Tests/install_deps.js
Normal file
7
lib/aura/Tests/install_deps.js
Normal file
@ -0,0 +1,7 @@
|
||||
const path = require("path");
|
||||
|
||||
const utils = require(path.join(__dirname, "utils.js"));
|
||||
|
||||
(async () => {
|
||||
await utils.install_deps();
|
||||
})();
|
71
lib/aura/Tests/khafile.js
Normal file
71
lib/aura/Tests/khafile.js
Normal file
@ -0,0 +1,71 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const utils = require(path.join(__dirname, "utils.js"));
|
||||
|
||||
const useInstrument = true;
|
||||
|
||||
async function run() {
|
||||
if (!fs.existsSync(".haxelib/")) {
|
||||
await utils.install_deps();
|
||||
}
|
||||
|
||||
const project = new Project('Aura Tests');
|
||||
|
||||
project.addSources(".");
|
||||
|
||||
await project.addProject("../");
|
||||
|
||||
project.addLibrary("utest");
|
||||
project.addDefine("UTEST_PRINT_TESTS");
|
||||
|
||||
// Easier to match problems with the problem matcher below, enable if
|
||||
// running headless in command line (this will prevent displaying the html output)
|
||||
// project.addDefine("UTEST_FAILURE_THROW");
|
||||
|
||||
if (useInstrument) {
|
||||
project.addLibrary("instrument");
|
||||
project.addDefine("instrument_quiet");
|
||||
project.addDefine("coverage-console-package-summary-reporter");
|
||||
// project.addDefine("coverage-console-summary-reporter");
|
||||
project.addParameter("--macro instrument.Instrumentation.coverage(['aura'], null, ['auratests'])");
|
||||
|
||||
// From https://github.com/HaxeFoundation/hxnodejs/blob/master/extraParams.hxml
|
||||
// to fix sys access error on nodejs even if it should work
|
||||
// Reference:
|
||||
// https://github.com/AlexHaxe/haxe-instrument/issues/8
|
||||
// https://github.com/HaxeFoundation/hxnodejs/issues/59
|
||||
// https://community.haxe.org/t/using-sys-in-nodejs-target/3702
|
||||
// project.addParameter("--macro allowPackage('sys')");
|
||||
// project.addParameter("--macro define('nodejs')");
|
||||
// project.addParameter("--macro _internal.SuppressDeprecated.run()");
|
||||
}
|
||||
|
||||
if (project.targetOptions.html5.expose === undefined) {
|
||||
project.targetOptions.html5.expose = "";
|
||||
}
|
||||
project.targetOptions.html5.expose += "logToMainProcess: (type, text) => electron.ipcRenderer.send('log-main', type, text),";
|
||||
|
||||
project.addParameter("--no-opt");
|
||||
project.addParameter("--no-inline");
|
||||
project.addParameter('-dce full');
|
||||
|
||||
// project.addParameter("--macro nullSafety('aura', Strict)");
|
||||
// project.addParameter("--macro nullSafety('aura', StrictThreaded)");
|
||||
|
||||
project.addDefine("AURA_UNIT_TESTS");
|
||||
project.addDefine("AURA_ASSERT_LEVEL=Debug");
|
||||
|
||||
project.addCDefine("KINC_NO_WAYLAND"); // Causes errors in the CI
|
||||
|
||||
callbacks.postBuild = () => {
|
||||
fs.copyFileSync("Data/index.html", "build/debug-html5/index.html");
|
||||
|
||||
const electronJSAppend = fs.readFileSync("Data/electron-append.js", "utf8");
|
||||
fs.appendFileSync("build/debug-html5/electron.js", "\n\n" + electronJSAppend);
|
||||
};
|
||||
|
||||
resolve(project);
|
||||
}
|
||||
|
||||
await run();
|
57
lib/aura/Tests/readme.md
Normal file
57
lib/aura/Tests/readme.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Aura Tests
|
||||
|
||||
The `/Tests` directory contains a test suite to test some of Aura's functionality.
|
||||
|
||||
Because Kha/Kinc currently cannot run in headless mode (see the relevant
|
||||
[issue](https://github.com/Kode/Kinc/issues/564)) in targets other than node.js
|
||||
and the node.js target breaks quite often due to Kha updates, the tests currently
|
||||
run semi-automatically (i.e. they are invoked by the user) on Kha's `debug-html5`
|
||||
target in Electron. As a consequence, it is currently not possible to run
|
||||
the tests in a CI pipeline.
|
||||
|
||||
## Setup
|
||||
|
||||
Running the tests requires node.js which you probably already have installed
|
||||
since it is required for running Khamake.
|
||||
|
||||
If you are using Leenkx, you can instead use the node.js executables included
|
||||
in the SDK at `<sdk-path>/nodejs`.
|
||||
|
||||
## Running the Tests
|
||||
|
||||
### Using VSCode/VSCodium
|
||||
|
||||
1. Add the `/Tests` directory as a folder to your VSCode workspace
|
||||
using `File > Add Folder to Workspace`. VSCode unfortunately doesn't search
|
||||
for task.json files in subdirectories.
|
||||
|
||||
2. Press `F1` or `Ctrl + Shift + P` and select `Tasks: Run Test Task`.
|
||||
|
||||
> **Note**<br>
|
||||
> The task automatically picks up the Kha version (and its corresponding
|
||||
> Electron version) as configured for the Kha extension for VSCode.
|
||||
|
||||
### From the Command Line
|
||||
|
||||
1. Point the environment variable `KHA_PATH` to the root path of the Kha repository.
|
||||
|
||||
2. Point the environment variable `ELECTRON_BIN` to an Electron executable.
|
||||
|
||||
3. Run the following on a command line opened in this `/Tests` directory:
|
||||
```batch
|
||||
node run.js
|
||||
```
|
||||
|
||||
## Updating Dependencies
|
||||
|
||||
The first time the test project is built, all necessary dependencies are
|
||||
automatically installed. If you want to update them to the newest version,
|
||||
simply run the following on a command line opened in this `/Tests` directory:
|
||||
```batch
|
||||
node install_deps.js
|
||||
```
|
||||
|
||||
## Defines
|
||||
|
||||
While the tests are run, the define `AURA_UNIT_TESTS` is set and the assertion
|
||||
level is set to `AURA_ASSERT_LEVEL=Debug`.
|
30
lib/aura/Tests/run.js
Normal file
30
lib/aura/Tests/run.js
Normal file
@ -0,0 +1,30 @@
|
||||
const path = require("path");
|
||||
|
||||
const utils = require(path.join(__dirname, "utils.js"));
|
||||
|
||||
async function run() {
|
||||
const khaPath = utils.getEnvVarSafe("KHA_PATH");
|
||||
const electron_bin = utils.getEnvVarSafe("ELECTRON_BIN");
|
||||
|
||||
khamake_args = [
|
||||
path.join(khaPath, "make"),
|
||||
"debug-html5",
|
||||
"--debug"
|
||||
]
|
||||
|
||||
electron_args = [
|
||||
"--no-sandbox",
|
||||
"--force-device-scale-factor=1",
|
||||
// "--enable-logging",
|
||||
// "--trace-warnings",
|
||||
"--force_low_power_gpu",
|
||||
"build/debug-html5/electron.js"
|
||||
]
|
||||
|
||||
await utils.spawnCommand("node", khamake_args, true);
|
||||
await utils.spawnCommand(electron_bin, electron_args, true);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await run();
|
||||
})();
|
58
lib/aura/Tests/utils.js
Normal file
58
lib/aura/Tests/utils.js
Normal file
@ -0,0 +1,58 @@
|
||||
const child_process = require("child_process");
|
||||
|
||||
async function spawnCommand(command, args, exitOnErr) {
|
||||
exitOnErr = exitOnErr === undefined ? true : exitOnErr;
|
||||
|
||||
const proc = child_process.spawn(command, args);
|
||||
|
||||
proc.stdout.on("data", function (data) {
|
||||
console.log(data.toString().trim());
|
||||
});
|
||||
|
||||
proc.stderr.on("data", function (data) {
|
||||
console.error(data.toString().trim());
|
||||
});
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
proc.on("close", function (code) {
|
||||
if (code == 0) {
|
||||
console.log(`Child process exited with code ${code}`);
|
||||
resolve();
|
||||
} else {
|
||||
console.error(`Child process failed with code ${code}`);
|
||||
if (exitOnErr) {
|
||||
process.exit(code);
|
||||
}
|
||||
reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getEnvVarSafe(varname) {
|
||||
const value = process.env[varname];
|
||||
if (value === undefined) {
|
||||
exitWithError(`Environment variable '${varname}' not set!`)
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function exitWithError(message, exitCode) {
|
||||
exitCode = exitCode === undefined ? 1 : exitCode;
|
||||
|
||||
console.error("[Error] " + message);
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
async function install_deps() {
|
||||
console.log("Downloading haxelib dependencies...");
|
||||
await spawnCommand("haxelib", ["newrepo"]);
|
||||
await spawnCommand("haxelib", ["install", "TestDeps.hxml", "--always"]);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
spawnCommand: spawnCommand,
|
||||
getEnvVarSafe: getEnvVarSafe,
|
||||
exitWithError: exitWithError,
|
||||
install_deps: install_deps,
|
||||
}
|
Reference in New Issue
Block a user