127 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			127 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
|  | 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; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | } |