Support audio worklets
This commit is contained in:
		
							parent
							
								
									9c26df784d
								
							
						
					
					
						commit
						880fd39ea9
					
				
							
								
								
									
										81
									
								
								blarf.js
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								blarf.js
									
									
									
									
									
								
							@ -16,47 +16,65 @@
 | 
			
		||||
	var CanvCtx = Canvus.getContext("2d")
 | 
			
		||||
	
 | 
			
		||||
	var AudCtx
 | 
			
		||||
	var AudScript
 | 
			
		||||
	var AudScript, AudWorklet
 | 
			
		||||
	var AudHz
 | 
			
		||||
	
 | 
			
		||||
	function create_audio(hz, channels) {
 | 
			
		||||
		if(AudCtx) {
 | 
			
		||||
			AudCtx.close()
 | 
			
		||||
			AudScript = null
 | 
			
		||||
			AudWorklet = null
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		AudHz = hz
 | 
			
		||||
		
 | 
			
		||||
		var DebugSine = 0
 | 
			
		||||
		
 | 
			
		||||
		AudCtx = new AudioContext({sampleRate: hz})
 | 
			
		||||
		AudScript = AudCtx.createScriptProcessor(1024, channels, channels)
 | 
			
		||||
		AudScript.onaudioprocess = function(e) {
 | 
			
		||||
			var outL = e.outputBuffer.getChannelData(0)
 | 
			
		||||
			var outR = channels > 1 ? e.outputBuffer.getChannelData(1) : null
 | 
			
		||||
			
 | 
			
		||||
			var leftToWrite = outL.length
 | 
			
		||||
			var offset = 0
 | 
			
		||||
			
 | 
			
		||||
			while(AudioQueue.length && leftToWrite) {
 | 
			
		||||
				var amount = Math.min(leftToWrite, AudioQueue[0].left.length)
 | 
			
		||||
		
 | 
			
		||||
		if(AudCtx.audioWorklet) {
 | 
			
		||||
			AudCtx.audioWorklet.addModule("rawpcmworklet.js").then(function() {
 | 
			
		||||
				AudWorklet = new AudioWorkletNode(AudCtx, "rawpcmworklet", {
 | 
			
		||||
					outputChannelCount: [2]
 | 
			
		||||
				})
 | 
			
		||||
				AudWorklet.connect(AudCtx.destination)
 | 
			
		||||
			})
 | 
			
		||||
		} else {
 | 
			
		||||
			AudScript = AudCtx.createScriptProcessor(4096, channels, channels)
 | 
			
		||||
			AudScript.onaudioprocess = function(e) {
 | 
			
		||||
				var outL = e.outputBuffer.getChannelData(0)
 | 
			
		||||
				var outR = channels > 1 ? e.outputBuffer.getChannelData(1) : null
 | 
			
		||||
				
 | 
			
		||||
				outL.set(AudioQueue[0].left.subarray(0, amount), offset)
 | 
			
		||||
				if(outR) outR.set(AudioQueue[0].right.subarray(0, amount), offset)
 | 
			
		||||
				
 | 
			
		||||
				AudioQueue[0].left = AudioQueue[0].left.subarray(amount)
 | 
			
		||||
				if(outR) AudioQueue[0].right = AudioQueue[0].right.subarray(amount)
 | 
			
		||||
				
 | 
			
		||||
				if(AudioQueue[0].left.length == 0) {
 | 
			
		||||
					AudioQueue.shift()
 | 
			
		||||
				/*for(var i = 0; i < outL.length; i++) {
 | 
			
		||||
					outL[i] = Math.sin(440 * 2 * 3.14159 * (DebugSine / AudHz))
 | 
			
		||||
					DebugSine++
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				leftToWrite -= amount
 | 
			
		||||
				offset += amount
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			if(RenderStartTime && leftToWrite) {
 | 
			
		||||
				buffering(1000)
 | 
			
		||||
				AudioQueue = []
 | 
			
		||||
				return*/
 | 
			
		||||
				
 | 
			
		||||
				var leftToWrite = outL.length
 | 
			
		||||
				var offset = 0
 | 
			
		||||
				
 | 
			
		||||
				while(AudioQueue.length && leftToWrite) {
 | 
			
		||||
					var amount = Math.min(leftToWrite, AudioQueue[0].left.length)
 | 
			
		||||
					
 | 
			
		||||
					outL.set(AudioQueue[0].left.subarray(0, amount), offset)
 | 
			
		||||
					if(outR) outR.set(AudioQueue[0].right.subarray(0, amount), offset)
 | 
			
		||||
					
 | 
			
		||||
					AudioQueue[0].left = AudioQueue[0].left.subarray(amount)
 | 
			
		||||
					if(outR) AudioQueue[0].right = AudioQueue[0].right.subarray(amount)
 | 
			
		||||
					
 | 
			
		||||
					if(AudioQueue[0].left.length == 0) {
 | 
			
		||||
						AudioQueue.shift()
 | 
			
		||||
					}
 | 
			
		||||
					
 | 
			
		||||
					leftToWrite -= amount
 | 
			
		||||
					offset += amount
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			AudScript.connect(AudCtx.destination)
 | 
			
		||||
		}
 | 
			
		||||
		AudScript.connect(AudCtx.destination)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var LastControlsInterrupt
 | 
			
		||||
@ -149,11 +167,6 @@
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var VideoBufferingOffset = 0
 | 
			
		||||
	function buffering(millis) {
 | 
			
		||||
		//var silence = new Float32Array(millis * 48);
 | 
			
		||||
		//AudioQueue.push({left: silence, right: silence})
 | 
			
		||||
		//VideoBufferingOffset += millis
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function toHex(buffer) {
 | 
			
		||||
		return Array.prototype.map.call(buffer, x => ('00' + x.toString(16)).slice(-2)).join('');
 | 
			
		||||
@ -386,6 +399,12 @@
 | 
			
		||||
		ws.onmessage = function(ev) {
 | 
			
		||||
			ebml.poosh(new Uint8Array(ev.data))
 | 
			
		||||
			ebml.parse()
 | 
			
		||||
			
 | 
			
		||||
			// It would make more sense for this to be in `render` but we need the guarantee that this will run when the tab is out of focus
 | 
			
		||||
			if(AudCtx.state == "running" && AudWorklet && AudioQueue.length) {
 | 
			
		||||
				AudWorklet.port.postMessage({msg: "data", audio: AudioQueue})
 | 
			
		||||
				AudioQueue.length = 0
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		ws.onclose = function(ev) {
 | 
			
		||||
			setTimeout(reconnect_ws, 5000)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										47
									
								
								rawpcmworklet.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								rawpcmworklet.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
// To explain succinctly, the people who designed AudioWorklet and
 | 
			
		||||
// deprecated ScriptProcessorNode are retarded and we need a worklet
 | 
			
		||||
// that does basically nothing to get glitchless audio.
 | 
			
		||||
 | 
			
		||||
class RawPCMWorklet extends AudioWorkletProcessor {
 | 
			
		||||
	constructor() {
 | 
			
		||||
		super()
 | 
			
		||||
		
 | 
			
		||||
		this.left = new Float32Array()
 | 
			
		||||
		this.right = new Float32Array()
 | 
			
		||||
 | 
			
		||||
		this.port.onmessage = (event) => {
 | 
			
		||||
			var newaudioframes = event.data.audio
 | 
			
		||||
			
 | 
			
		||||
			for(var i = 0; i < newaudioframes.length; i++) {
 | 
			
		||||
				var newleft = new Float32Array(this.left.length + newaudioframes[i].left.length)
 | 
			
		||||
				newleft.set(this.left, 0)
 | 
			
		||||
				newleft.set(newaudioframes[i].left, this.left.length)
 | 
			
		||||
				this.left = newleft
 | 
			
		||||
				
 | 
			
		||||
				var newright = new Float32Array(this.right.length + newaudioframes[i].right.length)
 | 
			
		||||
				newright.set(this.right, 0)
 | 
			
		||||
				newright.set(newaudioframes[i].right, this.right.length)
 | 
			
		||||
				this.right = newright
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	process(inputs, outputs, parameters) {
 | 
			
		||||
		const output = outputs[0]
 | 
			
		||||
		
 | 
			
		||||
		var left = output[0]
 | 
			
		||||
		var right = output[1]
 | 
			
		||||
		
 | 
			
		||||
		var available = Math.min(left.length, this.left.length)
 | 
			
		||||
		
 | 
			
		||||
		left.set(this.left.slice(0, available))
 | 
			
		||||
		right.set(this.right.slice(0, available))
 | 
			
		||||
		
 | 
			
		||||
		this.left = this.left.slice(available)
 | 
			
		||||
		this.right = this.right.slice(available)
 | 
			
		||||
		
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
registerProcessor('rawpcmworklet', RawPCMWorklet);
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user