198 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			198 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
|  | package kha.audio2; | ||
|  | 
 | ||
|  | #if cpp | ||
|  | import sys.thread.Mutex; | ||
|  | #end | ||
|  | import haxe.ds.Vector; | ||
|  | 
 | ||
|  | class Audio1 { | ||
|  | 	static inline var channelCount: Int = 32; | ||
|  | 	static var soundChannels: Vector<AudioChannel>; | ||
|  | 	static var streamChannels: Vector<StreamChannel>; | ||
|  | 
 | ||
|  | 	static var internalSoundChannels: Vector<AudioChannel>; | ||
|  | 	static var internalStreamChannels: Vector<StreamChannel>; | ||
|  | 	static var sampleCache1: kha.arrays.Float32Array; | ||
|  | 	static var sampleCache2: kha.arrays.Float32Array; | ||
|  | 	static var lastAllocationCount: Int = 0; | ||
|  | 
 | ||
|  | 	#if cpp | ||
|  | 	static var mutex: Mutex; | ||
|  | 	#end | ||
|  | 
 | ||
|  | 	@:noCompletion | ||
|  | 	public static function _init(): Void { | ||
|  | 		#if cpp | ||
|  | 		mutex = new Mutex(); | ||
|  | 		#end | ||
|  | 		soundChannels = new Vector<AudioChannel>(channelCount); | ||
|  | 		streamChannels = new Vector<StreamChannel>(channelCount); | ||
|  | 		internalSoundChannels = new Vector<AudioChannel>(channelCount); | ||
|  | 		internalStreamChannels = new Vector<StreamChannel>(channelCount); | ||
|  | 		sampleCache1 = new kha.arrays.Float32Array(512); | ||
|  | 		sampleCache2 = new kha.arrays.Float32Array(512); | ||
|  | 		lastAllocationCount = 0; | ||
|  | 		Audio.audioCallback = mix; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function max(a: Float, b: Float): Float { | ||
|  | 		return a > b ? a : b; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function min(a: Float, b: Float): Float { | ||
|  | 		return a < b ? a : b; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function mix(samplesBox: kha.internal.IntBox, buffer: Buffer): Void { | ||
|  | 		var samples = samplesBox.value; | ||
|  | 		if (sampleCache1.length < samples) { | ||
|  | 			if (Audio.disableGcInteractions) { | ||
|  | 				trace("Unexpected allocation request in audio thread."); | ||
|  | 				for (i in 0...samples) { | ||
|  | 					buffer.data.set(buffer.writeLocation, 0); | ||
|  | 					buffer.writeLocation += 1; | ||
|  | 					if (buffer.writeLocation >= buffer.size) { | ||
|  | 						buffer.writeLocation = 0; | ||
|  | 					} | ||
|  | 				} | ||
|  | 				lastAllocationCount = 0; | ||
|  | 				Audio.disableGcInteractions = false; | ||
|  | 				return; | ||
|  | 			} | ||
|  | 			sampleCache1 = new kha.arrays.Float32Array(samples * 2); | ||
|  | 			sampleCache2 = new kha.arrays.Float32Array(samples * 2); | ||
|  | 			lastAllocationCount = 0; | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			if (lastAllocationCount > 100) { | ||
|  | 				Audio.disableGcInteractions = true; | ||
|  | 			} | ||
|  | 			else { | ||
|  | 				lastAllocationCount += 1; | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		for (i in 0...samples) { | ||
|  | 			sampleCache2[i] = 0; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		#if cpp | ||
|  | 		mutex.acquire(); | ||
|  | 		#end | ||
|  | 		for (i in 0...channelCount) { | ||
|  | 			internalSoundChannels[i] = soundChannels[i]; | ||
|  | 		} | ||
|  | 		for (i in 0...channelCount) { | ||
|  | 			internalStreamChannels[i] = streamChannels[i]; | ||
|  | 		} | ||
|  | 		#if cpp | ||
|  | 		mutex.release(); | ||
|  | 		#end | ||
|  | 
 | ||
|  | 		for (channel in internalSoundChannels) { | ||
|  | 			if (channel == null || channel.finished) | ||
|  | 				continue; | ||
|  | 			channel.nextSamples(sampleCache1, samples, buffer.samplesPerSecond); | ||
|  | 			for (i in 0...samples) { | ||
|  | 				sampleCache2[i] += sampleCache1[i] * channel.volume; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		for (channel in internalStreamChannels) { | ||
|  | 			if (channel == null || channel.finished) | ||
|  | 				continue; | ||
|  | 			channel.nextSamples(sampleCache1, samples, buffer.samplesPerSecond); | ||
|  | 			for (i in 0...samples) { | ||
|  | 				sampleCache2[i] += sampleCache1[i] * channel.volume; | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		for (i in 0...samples) { | ||
|  | 			buffer.data.set(buffer.writeLocation, max(min(sampleCache2[i], 1.0), -1.0)); | ||
|  | 			buffer.writeLocation += 1; | ||
|  | 			if (buffer.writeLocation >= buffer.size) { | ||
|  | 				buffer.writeLocation = 0; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function play(sound: Sound, loop: Bool = false): kha.audio1.AudioChannel { | ||
|  | 		var channel: kha.audio2.AudioChannel = null; | ||
|  | 		if (Audio.samplesPerSecond != sound.sampleRate) { | ||
|  | 			channel = new ResamplingAudioChannel(loop, sound.sampleRate); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			#if sys_ios | ||
|  | 			channel = new ResamplingAudioChannel(loop, sound.sampleRate); | ||
|  | 			#else | ||
|  | 			channel = new AudioChannel(loop); | ||
|  | 			#end | ||
|  | 		} | ||
|  | 		channel.data = sound.uncompressedData; | ||
|  | 		var foundChannel = false; | ||
|  | 
 | ||
|  | 		#if cpp | ||
|  | 		mutex.acquire(); | ||
|  | 		#end | ||
|  | 		for (i in 0...channelCount) { | ||
|  | 			if (soundChannels[i] == null || soundChannels[i].finished) { | ||
|  | 				soundChannels[i] = channel; | ||
|  | 				foundChannel = true; | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		#if cpp | ||
|  | 		mutex.release(); | ||
|  | 		#end | ||
|  | 
 | ||
|  | 		return foundChannel ? channel : null; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function _playAgain(channel: kha.audio2.AudioChannel): Void { | ||
|  | 		#if cpp | ||
|  | 		mutex.acquire(); | ||
|  | 		#end | ||
|  | 		for (i in 0...channelCount) { | ||
|  | 			if (soundChannels[i] == channel) { | ||
|  | 				soundChannels[i] = null; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		for (i in 0...channelCount) { | ||
|  | 			if (soundChannels[i] == null || soundChannels[i].finished || soundChannels[i] == channel) { | ||
|  | 				soundChannels[i] = channel; | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		#if cpp | ||
|  | 		mutex.release(); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function stream(sound: Sound, loop: Bool = false): kha.audio1.AudioChannel { | ||
|  | 		{ | ||
|  | 			// try to use hardware accelerated audio decoding | ||
|  | 			var hardwareChannel = Audio.stream(sound, loop); | ||
|  | 			if (hardwareChannel != null) | ||
|  | 				return hardwareChannel; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var channel: StreamChannel = new StreamChannel(sound.compressedData, loop); | ||
|  | 		var foundChannel = false; | ||
|  | 
 | ||
|  | 		#if cpp | ||
|  | 		mutex.acquire(); | ||
|  | 		#end | ||
|  | 		for (i in 0...channelCount) { | ||
|  | 			if (streamChannels[i] == null || streamChannels[i].finished) { | ||
|  | 				streamChannels[i] = channel; | ||
|  | 				foundChannel = true; | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		#if cpp | ||
|  | 		mutex.release(); | ||
|  | 		#end | ||
|  | 
 | ||
|  | 		return foundChannel ? channel : null; | ||
|  | 	} | ||
|  | } |