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;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |