Compare commits

..

No commits in common. "2fffb905e303576b7aad721e99892722ac68a101" and "880fd39ea98bf63bcc5e435eedc6ba77c58cf7be" have entirely different histories.

3 changed files with 50 additions and 128 deletions

View File

@ -31,7 +31,6 @@
var DebugSine = 0
AudCtx = new AudioContext({sampleRate: hz})
AudCtx.suspend()
if(AudCtx.audioWorklet) {
AudCtx.audioWorklet.addModule("rawpcmworklet.js").then(function() {
@ -46,6 +45,14 @@
var outL = e.outputBuffer.getChannelData(0)
var outR = channels > 1 ? e.outputBuffer.getChannelData(1) : null
/*for(var i = 0; i < outL.length; i++) {
outL[i] = Math.sin(440 * 2 * 3.14159 * (DebugSine / AudHz))
DebugSine++
}
AudioQueue = []
return*/
var leftToWrite = outL.length
var offset = 0
@ -103,21 +110,6 @@
var RenderStartTime, VideoStartTime
function crop_audio_queue(durationToRemove) {
while(AudioQueue.length && durationToRemove) {
var amount = Math.min(durationToRemove, AudioQueue[0].left.length)
AudioQueue[0].left = AudioQueue[0].left.subarray(amount)
AudioQueue[0].right = AudioQueue[0].right.subarray(amount)
if(AudioQueue[0].left.length == 0) {
AudioQueue.shift()
}
durationToRemove -= amount
}
}
var Statistics = {}
var TheWorker = new Worker("blarfwork.js")
TheWorker.onmessage = function(e) {
@ -130,20 +122,25 @@
// Audio may be loaded but it might not play because of autoplay permissions
// In this case the audio queue will fill up and cause ever-increasing AV desync
// To prevent this, manually crop the audio to the duration in the video queue
if(AudCtx && AudCtx.state != "running") {
var durationInAudioQueue = AudioQueue.length ? AudioQueue.reduce((acc, el) => acc + el.left.length, 0) : 0
//if(AudWorklet) {
// // With audio worklets, crop to 1024 samples max to prevent ring buffer overflow
//
// crop_audio_queue(Math.max(durationInAudioQueue - 1024, 0))
//} else {
// // Without audio worklets we use a FIFO and can crop to the duration in the video queue
//
var durationToRemove = Math.max(durationInAudioQueue - (VideoQueue.length ? (VideoQueue[VideoQueue.length - 1].t - VideoQueue[0].t) : 0) * AudHz / 1000, 0)
//
crop_audio_queue(durationToRemove)
//}
var durationToRemove = Math.max(durationInAudioQueue - (VideoQueue.length ? (VideoQueue[VideoQueue.length - 1].t - VideoQueue[0].t) : 0) * AudHz / 1000, 0)
while(AudioQueue.length && durationToRemove) {
var amount = Math.min(durationToRemove, AudioQueue[0].left.length)
AudioQueue[0].left = AudioQueue[0].left.subarray(amount)
AudioQueue[0].right = AudioQueue[0].left.subarray(amount)
if(AudioQueue[0].left.length == 0) {
AudioQueue.shift()
}
durationToRemove -= amount
}
}
}
@ -161,7 +158,7 @@
text = text + k + ":" + (Math.floor(100 * Statistics[k].sum / Statistics[k].count) / 100) + ","
}
stats.innerText = text*/
stats.innerText = (VideoQueue.length ? (VideoQueue[VideoQueue.length - 1].t - VideoQueue[0].t) : "0") + "v" + (AudioQueue.reduce(function(acc, obj) {return acc + obj.left.length * 1000 / AudHz}, 0)|0) + "a"
stats.innerHTML = (VideoQueue.length ? (VideoQueue[VideoQueue.length - 1].t - VideoQueue[0].t) : "0") + "v" + (AudioQueue.reduce(function(acc, obj) {return acc + obj.left.length * 1000 / AudHz}, 0)|0) + "a"
}
}
@ -396,27 +393,6 @@
ebml.ondata = matr.ondata.bind(matr)
ebml.onexit = matr.onexit.bind(matr)
function merge_audio_queue() {
var s = 0
for(var i = 0; i < AudioQueue.length; i++) {
s += AudioQueue[i].left.length
}
var L = new Float32Array(s)
var R = new Float32Array(s)
s = 0
for(var i = 0; i < AudioQueue.length; i++) {
L.set(AudioQueue[i].left, s)
R.set(AudioQueue[i].right, s)
s += AudioQueue[i].left.length
}
return {msg: "data", left: L, right: R}
}
function reconnect_ws() {
var ws = new WebSocket(BlarfEl.getAttribute("data-target"))
ws.binaryType = "arraybuffer"
@ -426,14 +402,9 @@
// 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(merge_audio_queue())
AudWorklet.port.postMessage({msg: "data", audio: AudioQueue})
AudioQueue.length = 0
}
if(VideoQueue.length) {
while(document.timeline.currentTime - VideoQueue[0].t > 5000) {
VideoQueue.shift()
}
}
}
ws.onclose = function(ev) {
setTimeout(reconnect_ws, 5000)

13
main2.c
View File

@ -101,18 +101,15 @@ static void consume(Client *cli, size_t n) {
cli->len -= n;
}
static int transmit(Client *cli, const char *buf, size_t sz) {
static void transmit(Client *cli, const char *buf, size_t sz) {
while(sz) {
ssize_t s = send(cli->fd, buf, sz, MSG_NOSIGNAL);
ssize_t s = send(cli->fd, buf, sz, 0);
if(s >= 0) {
buf += s;
sz -= s;
} else {
return 0;
}
}
return 1;
}
static void transmit_all(const char *buf, size_t sz) {
@ -309,9 +306,7 @@ static int handle(Client *cli) {
size_t rsize = cli->len;
int pret = phr_decode_chunked(&cli->chudec, cli->buf, &rsize);
if(pret == -1) {
return 0;
}
if(pret == -1) return 0;
stream_step(cli->buf, rsize);
@ -372,8 +367,6 @@ static int handle(Client *cli) {
cli->ws.incoming = realloc(cli->ws.incoming, cli->ws.incomingSz + payloadSz);
memcpy(cli->ws.incoming + cli->ws.incomingSz, cli->buf + i + 4, payloadSz);
consume(cli, i + 4 + payloadSz);
if(fin) {
receive_ws(cli);

View File

@ -1,50 +1,28 @@
// To explain succinctly, the people who designed AudioWorklet and
// deprecated ScriptProcessorNode are retarded and we need a worklet
// that does basically nothing
// Must be careful to create as little garbage as possible otherwise
// even this will produce cracks/pops/clicks/blips.
// It was like this on Firefox; Chromium managed.
// that does basically nothing to get glitchless audio.
class RawPCMWorklet extends AudioWorkletProcessor {
constructor() {
super()
this.ringL = new Float32Array(65536)
this.ringR = new Float32Array(65536)
this.ringWrite = 0
this.ringRead = 0
for(var z = 0; z < 65536; z++) {
this.ringL[z] = Math.sin(z / 128 * 2 * Math.PI) * 0.3
}
this.left = new Float32Array()
this.right = new Float32Array()
this.port.onmessage = (event) => {
var newaudioframes = event.data
var newaudioframes = event.data.audio
var newlen = newaudioframes.left.length
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
if(newaudioframes.left.length > this.ringL.length) {
newaudioframes.left = newaudioframes.left.slice(newaudioframes.left.length - this.ringL.length)
newaudioframes.right = newaudioframes.right.slice(newaudioframes.right.length - this.ringL.length)
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
}
if(this.ringWrite % this.ringL.length + newaudioframes.left.length <= this.ringL.length) {
this.ringL.set(newaudioframes.left, this.ringWrite % this.ringL.length)
this.ringR.set(newaudioframes.right, this.ringWrite % this.ringL.length)
} else {
var boundary = this.ringL.length - this.ringWrite % this.ringL.length
this.ringL.set(newaudioframes.left.slice(0, boundary), this.ringWrite % this.ringL.length)
this.ringL.set(newaudioframes.left.slice(boundary), 0)
this.ringR.set(newaudioframes.right.slice(0, boundary), this.ringWrite % this.ringL.length)
this.ringR.set(newaudioframes.right.slice(boundary), 0)
}
this.ringWrite += newlen
console.log(this.ringWrite - this.ringRead)
}
}
@ -54,35 +32,15 @@ class RawPCMWorklet extends AudioWorkletProcessor {
var left = output[0]
var right = output[1]
/*if(this.ringWrite < 16384) {
return true
}*/
var available = Math.min(left.length, this.left.length)
var available = Math.min(left.length, Math.max(0, this.ringWrite - this.ringRead))
left.set(this.left.slice(0, available))
right.set(this.right.slice(0, available))
if(this.ringRead % this.ringL.length + available <= this.ringL.length) {
left.set(this.ringL.slice(this.ringRead % this.ringL.length, this.ringRead % this.ringL.length + available))
right.set(this.ringR.slice(this.ringRead % this.ringL.length, this.ringRead % this.ringL.length + available))
} else {
left.set(this.ringL.slice(this.ringRead % this.ringL.length))
right.set(this.ringR.slice(this.ringRead % this.ringL.length))
this.left = this.left.slice(available)
this.right = this.right.slice(available)
var boundary = this.ringL.length - this.ringRead % this.ringL.length
left.set(this.ringL.slice(0, available - boundary), boundary)
right.set(this.ringR.slice(0, available - boundary), boundary)
}
this.ringRead += left.length
/*for(var s = 0; s < available; s++) {
var sw = Math.sin((this.debug + s) / 48000 * 440 * 2 * 3.1415926) * 0.3
left[s] = sw
right[s] = sw
}
this.debug += available*/
return true
return true;
}
}