Spaces:
Sleeping
Sleeping
File size: 1,747 Bytes
96f2542 | 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 | export type RecorderState = "idle" | "requesting" | "recording" | "stopping" | "error";
type Deps = {
getUserMedia?: (constraints: MediaStreamConstraints) => Promise<MediaStream>;
};
export class Recorder {
state: RecorderState = "idle";
lastError: Error | null = null;
private chunks: BlobPart[] = [];
private rec: MediaRecorder | null = null;
private stream: MediaStream | null = null;
private getUserMedia: NonNullable<Deps["getUserMedia"]>;
constructor(deps: Deps = {}) {
this.getUserMedia =
deps.getUserMedia ?? ((c) => navigator.mediaDevices.getUserMedia(c));
}
requestStart() {
this.state = "requesting";
}
async start(): Promise<void> {
this.requestStart();
try {
this.stream = await this.getUserMedia({ audio: true });
} catch (e) {
this.lastError = e as Error;
this.state = "error";
throw e;
}
this.chunks = [];
this.rec = new MediaRecorder(this.stream);
this.rec.ondataavailable = (ev) => {
if (ev.data && ev.data.size > 0) this.chunks.push(ev.data);
};
this.rec.start();
this.state = "recording";
}
stop(): Promise<Blob | null> {
if (this.state === "idle") return Promise.resolve(null);
return new Promise((resolve) => {
if (!this.rec) {
this.state = "idle";
resolve(null);
return;
}
this.state = "stopping";
this.rec.onstop = () => {
const blob = new Blob(this.chunks, { type: this.rec?.mimeType ?? "audio/webm" });
this.chunks = [];
this.rec = null;
this.stream?.getTracks().forEach((t) => t.stop());
this.stream = null;
this.state = "idle";
resolve(blob);
};
this.rec.stop();
});
}
}
|