77 lines
2.7 KiB
TypeScript
77 lines
2.7 KiB
TypeScript
import { UINode } from "./node";
|
|
|
|
export class AudioRecorder extends UINode {
|
|
private audioElement: HTMLAudioElement;
|
|
private mediaRecorder: MediaRecorder | null;
|
|
private audioChunks: Blob[];
|
|
private stream: MediaStream | null;
|
|
private recording?: Blob;
|
|
|
|
public constructor(title: string) {
|
|
super(title);
|
|
this.audioElement = document.createElement("audio");
|
|
this.mediaRecorder = null;
|
|
this.audioChunks = [];
|
|
this.stream = null;
|
|
|
|
this.audioElement.setAttribute("controls", "true");
|
|
this.audioElement.setAttribute("aria-label", title);
|
|
this.element.appendChild(this.audioElement);
|
|
|
|
this.setRole("audio-recorder");
|
|
}
|
|
|
|
public async startRecording() {
|
|
try {
|
|
this.stream = await navigator.mediaDevices.getUserMedia({ audio: { autoGainControl: true, channelCount: 2, echoCancellation: false, noiseSuppression: false } });
|
|
this.mediaRecorder = new MediaRecorder(this.stream);
|
|
this.mediaRecorder.ondataavailable = (event) => {
|
|
this.audioChunks.push(event.data);
|
|
};
|
|
this.mediaRecorder.onstop = () => {
|
|
const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' });
|
|
this.recording = audioBlob;
|
|
this.audioChunks = [];
|
|
const audioUrl = URL.createObjectURL(audioBlob);
|
|
this.audioElement.src = audioUrl;
|
|
this.triggerRecordingComplete(audioUrl);
|
|
};
|
|
this.mediaRecorder.start();
|
|
} catch (error) {
|
|
console.error("Error accessing microphone:", error);
|
|
}
|
|
}
|
|
|
|
public stopRecording() {
|
|
if (this.mediaRecorder && this.mediaRecorder.state !== "inactive") {
|
|
this.mediaRecorder.stop();
|
|
}
|
|
if (this.stream) {
|
|
this.stream.getTracks().forEach(track => track.stop());
|
|
this.stream = null;
|
|
}
|
|
}
|
|
|
|
public getElement(): HTMLElement {
|
|
return this.element;
|
|
}
|
|
|
|
public onRecordingComplete(callback: (audioUrl: string) => void) {
|
|
this.element.addEventListener("recording-complete", (event: Event) => {
|
|
const customEvent = event as CustomEvent;
|
|
callback(customEvent.detail.audioUrl);
|
|
});
|
|
return this;
|
|
}
|
|
|
|
protected triggerRecordingComplete(audioUrl: string) {
|
|
const event = new CustomEvent("recording-complete", { detail: { audioUrl } });
|
|
this.element.dispatchEvent(event);
|
|
return this;
|
|
}
|
|
|
|
public getRecording() {
|
|
return this.recording;
|
|
}
|
|
}
|