SotiproAlpha2 / src /lib /audio-recorder.ts
Ezmary's picture
Update src/lib/audio-recorder.ts
36348bf verified
raw
history blame
3.57 kB
// src/lib/audio-recorder.ts
import EventEmitter from 'eventemitter3';
import { audioContext } from './utils';
import VolMeterWorket from './worklets/vol-meter';
export class AudioRecorder extends EventEmitter {
public recording = false;
private mediaStream: MediaStream | null = null;
private audioContext: AudioContext | null = null;
private processor: ScriptProcessorNode | null = null;
private source: MediaStreamAudioSourceNode | null = null;
private volMeter: AudioWorkletNode | null = null;
constructor() {
super();
}
private async init() {
if (!this.audioContext) {
this.audioContext = await audioContext({ id: 'audio-in' });
}
if (!this.mediaStream) {
try {
this.mediaStream = await navigator.mediaDevices.getUserMedia({
audio: {
sampleRate: 16000,
channelCount: 1,
echoCancellation: true,
noiseSuppression: true,
},
video: false,
});
} catch (err) {
console.error('Error getting media stream:', err);
this.emit('error', err);
return;
}
}
}
public async start() {
if (this.recording) {
return;
}
await this.init();
if (!this.audioContext || !this.mediaStream) {
console.error("AudioContext or MediaStream not available.");
return;
}
this.recording = true;
this.source = this.audioContext.createMediaStreamSource(this.mediaStream);
this.processor = this.audioContext.createScriptProcessor(1024, 1, 1);
this.processor.onaudioprocess = (e) => {
if (!this.recording) return;
const inputData = e.inputBuffer.getChannelData(0);
const pcm16 = this.floatTo16BitPCM(inputData);
const base64 = this.pcm16ToBase64(pcm16);
this.emit('data', base64);
};
// --- بخش کلیدی: اضافه کردن Volume Meter ---
try {
await this.audioContext.audioWorklet.addModule(VolMeterWorket);
this.volMeter = new AudioWorkletNode(this.audioContext, 'vumeter');
this.volMeter.port.onmessage = (event) => {
if(event.data.volume) {
// ارسال رویداد جدید به همراه حجم صدا
this.emit('volume', event.data.volume);
}
};
this.source.connect(this.volMeter);
} catch(err) {
console.error("Error adding AudioWorklet module", err);
}
// ------------------------------------------
this.source.connect(this.processor);
this.processor.connect(this.audioContext.destination);
this.emit('start');
}
public stop() {
if (!this.recording) {
return;
}
this.recording = false;
this.emit('stop');
this.source?.disconnect();
this.processor?.disconnect();
this.volMeter?.disconnect();
this.source = null;
this.processor = null;
this.volMeter = null;
}
// توابع کمکی برای تبدیل فرمت صدا
private floatTo16BitPCM(input: Float32Array): Int16Array {
const output = new Int16Array(input.length);
for (let i = 0; i < input.length; i++) {
const s = Math.max(-1, Math.min(1, input[i]));
output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
}
return output;
}
private pcm16ToBase64(pcm16: Int16Array): string {
const buffer = pcm16.buffer;
const bytes = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
}