Initial move
This commit is contained in:
76
frontend/src/ui/audio-recorder.ts
Normal file
76
frontend/src/ui/audio-recorder.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user