-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathws-codec-util.mjs
More file actions
111 lines (100 loc) · 2.51 KB
/
ws-codec-util.mjs
File metadata and controls
111 lines (100 loc) · 2.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { resample } from './resample.mjs';
export class FakeAudioData {
constructor() {
this.data = null;
this.buffer = {
getChannelData: n => {
return this.data;
},
};
}
set(data) {
this.data = data;
}
}
export class FakeIteratorResult {
constructor(value) {
this.value = value;
this.done = false;
}
setDone(done) {
this.done = done;
}
}
export class WsMediaStreamAudioReader {
constructor(mediaStream, {
audioContext,
// if passed, resample audio to this rate
// otherwise, use the rate of the audio context
sampleRate = undefined,
}) {
if (!audioContext) {
console.warn('need audio context');
debugger;
}
this.buffers = [];
this.cbs = [];
this.fakeAudioData = new FakeAudioData();
this.fakeIteratorResult = new FakeIteratorResult(this.fakeAudioData);
const mediaStreamSourceNode = audioContext.createMediaStreamSource(mediaStream);
const audioWorkletNode = new AudioWorkletNode(audioContext, 'ws-input-worklet');
audioWorkletNode.onprocessorerror = err => {
console.warn('audio worklet error', err);
};
audioWorkletNode.port.onmessage = e => {
let f32 = e.data;
// console.warn('push audio data', f32, audioContext.sampleRate, sampleRate);
if (sampleRate !== undefined) {
f32 = resample(f32, audioContext.sampleRate, sampleRate);
}
this.pushAudioData(f32);
};
mediaStreamSourceNode.connect(audioWorkletNode);
const close = e => {
this.cancel();
};
mediaStream.addEventListener('close', close);
this.cleanup = () => {
mediaStream.removeEventListener('close', close);
};
}
read() {
if (this.buffers.length > 0) {
const b = this.buffers.shift();
if (b) {
this.fakeAudioData.set(b);
} else {
this.fakeIteratorResult.setDone(true);
}
return Promise.resolve(this.fakeIteratorResult);
} else {
let accept;
const p = new Promise((a, r) => {
accept = a;
});
this.cbs.push(b => {
if (b) {
this.fakeAudioData.set(b);
} else {
this.fakeIteratorResult.setDone(true);
}
accept(this.fakeIteratorResult);
});
return p;
}
}
cancel() {
this.pushAudioData(null);
this.cleanup();
}
pushAudioData(b) {
if (this.cbs.length > 0) {
this.cbs.shift()(b);
} else {
this.buffers.push(b);
}
}
}
export function WsEncodedAudioChunk(o) {
return o;
}