Compare commits
No commits in common. "2fffb905e303576b7aad721e99892722ac68a101" and "880fd39ea98bf63bcc5e435eedc6ba77c58cf7be" have entirely different histories.
2fffb905e3
...
880fd39ea9
79
blarf.js
79
blarf.js
@ -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)
|
||||
//}
|
||||
|
||||
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
13
main2.c
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user