forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			127 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
			
		
		
	
	
			127 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| 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;
 | |
| 	}
 | |
| }
 |