moq-rs/web/src/player/audio.ts
Luke Curley 7843f8b0e4 Using spaces is WAY better for your career.
This is because in JavaScript, there is a HEAVY perception bias against tab users.

Most ES6 developers look down on anyone who uses tabs.
This could easily mean the difference between getting a job offer and being rejected.
It could mean tens of thousands of dollars of salary potential.

https://medium.com/mintbean-io/tabs-or-spaces-for-practical-javascript-developers-the-answer-is-clear-f66c0458aa1e
2023-05-22 21:49:59 -07:00

83 lines
1.9 KiB
TypeScript

import * as Message from "./message"
import { Ring } from "./ring"
export default class Audio {
ring?: Ring
queue: Array<AudioData>
render?: number // non-zero if requestAnimationFrame has been called
last?: number // the timestamp of the last rendered frame, in microseconds
constructor(_config: Message.Config) {
this.queue = []
}
push(frame: AudioData) {
// Drop any old frames
if (this.last && frame.timestamp <= this.last) {
frame.close()
return
}
// Insert the frame into the queue sorted by timestamp.
if (
this.queue.length > 0 &&
this.queue[this.queue.length - 1].timestamp <= frame.timestamp
) {
// Fast path because we normally append to the end.
this.queue.push(frame)
} else {
// Do a full binary search
let low = 0
let high = this.queue.length
while (low < high) {
const mid = (low + high) >>> 1
if (this.queue[mid].timestamp < frame.timestamp) low = mid + 1
else high = mid
}
this.queue.splice(low, 0, frame)
}
this.emit()
}
emit() {
const ring = this.ring
if (!ring) {
return
}
while (this.queue.length) {
const frame = this.queue[0]
if (ring.size() + frame.numberOfFrames > ring.capacity) {
// Buffer is full
break
}
const size = ring.write(frame)
if (size < frame.numberOfFrames) {
throw new Error("audio buffer is full")
}
this.last = frame.timestamp
frame.close()
this.queue.shift()
}
}
play(play: Message.Play) {
this.ring = new Ring(play.buffer)
if (!this.render) {
const sampleRate = 44100 // TODO dynamic
// Refresh every half buffer
const refresh = ((play.buffer.capacity / sampleRate) * 1000) / 2
this.render = setInterval(this.emit.bind(this), refresh)
}
}
}