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
83 lines
1.9 KiB
TypeScript
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)
|
|
}
|
|
}
|
|
}
|