ciyidogan commited on
Commit
e4f66ef
·
verified ·
1 Parent(s): 299226b

Create audio-stream.service.ts

Browse files
flare-ui/src/app/services/audio-stream.service.ts ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable } from '@angular/core';
2
+ import { Subject, Observable } from 'rxjs';
3
+
4
+ export interface AudioChunk {
5
+ data: string; // base64 encoded audio
6
+ timestamp: number;
7
+ }
8
+
9
+ @Injectable({
10
+ providedIn: 'root'
11
+ })
12
+ export class AudioStreamService {
13
+ private mediaStream: MediaStream | null = null;
14
+ private audioContext: AudioContext | null = null;
15
+ private processor: ScriptProcessorNode | null = null;
16
+ private source: MediaStreamAudioSourceNode | null = null;
17
+ private isRecording = false;
18
+
19
+ private audioChunkSubject = new Subject<AudioChunk>();
20
+ public audioChunk$ = this.audioChunkSubject.asObservable();
21
+
22
+ private errorSubject = new Subject<string>();
23
+ public error$ = this.errorSubject.asObservable();
24
+
25
+ constructor() {}
26
+
27
+ async startRecording(): Promise<void> {
28
+ try {
29
+ // Request microphone access
30
+ this.mediaStream = await navigator.mediaDevices.getUserMedia({
31
+ audio: {
32
+ echoCancellation: true,
33
+ noiseSuppression: true,
34
+ autoGainControl: true,
35
+ sampleRate: 16000
36
+ }
37
+ });
38
+
39
+ // Create audio context
40
+ this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)({
41
+ sampleRate: 16000
42
+ });
43
+
44
+ // Create audio source
45
+ this.source = this.audioContext.createMediaStreamSource(this.mediaStream);
46
+
47
+ // Create script processor for capturing audio
48
+ this.processor = this.audioContext.createScriptProcessor(4096, 1, 1);
49
+
50
+ this.processor.onaudioprocess = (e) => {
51
+ if (this.isRecording) {
52
+ const audioData = e.inputBuffer.getChannelData(0);
53
+ const audioChunk = this.convertToBase64(audioData);
54
+
55
+ this.audioChunkSubject.next({
56
+ data: audioChunk,
57
+ timestamp: Date.now()
58
+ });
59
+ }
60
+ };
61
+
62
+ // Connect nodes
63
+ this.source.connect(this.processor);
64
+ this.processor.connect(this.audioContext.destination);
65
+
66
+ this.isRecording = true;
67
+ console.log('🎤 Recording started');
68
+
69
+ } catch (error) {
70
+ console.error('Failed to start recording:', error);
71
+ this.errorSubject.next('Mikrofon erişimi sağlanamadı. Lütfen izinleri kontrol edin.');
72
+ throw error;
73
+ }
74
+ }
75
+
76
+ stopRecording(): void {
77
+ if (this.processor) {
78
+ this.processor.disconnect();
79
+ this.processor = null;
80
+ }
81
+
82
+ if (this.source) {
83
+ this.source.disconnect();
84
+ this.source = null;
85
+ }
86
+
87
+ if (this.audioContext) {
88
+ this.audioContext.close();
89
+ this.audioContext = null;
90
+ }
91
+
92
+ if (this.mediaStream) {
93
+ this.mediaStream.getTracks().forEach(track => track.stop());
94
+ this.mediaStream = null;
95
+ }
96
+
97
+ this.isRecording = false;
98
+ console.log('🎤 Recording stopped');
99
+ }
100
+
101
+ private convertToBase64(audioData: Float32Array): string {
102
+ // Convert float32 to int16
103
+ const length = audioData.length;
104
+ const buffer = new ArrayBuffer(length * 2);
105
+ const view = new DataView(buffer);
106
+
107
+ for (let i = 0; i < length; i++) {
108
+ const sample = Math.max(-1, Math.min(1, audioData[i]));
109
+ view.setInt16(i * 2, sample * 0x7FFF, true);
110
+ }
111
+
112
+ // Convert to base64
113
+ const uint8Array = new Uint8Array(buffer);
114
+ let binary = '';
115
+ for (let i = 0; i < uint8Array.byteLength; i++) {
116
+ binary += String.fromCharCode(uint8Array[i]);
117
+ }
118
+
119
+ return btoa(binary);
120
+ }
121
+
122
+ isCurrentlyRecording(): boolean {
123
+ return this.isRecording;
124
+ }
125
+
126
+ // Utility method to check browser support
127
+ static checkBrowserSupport(): boolean {
128
+ return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
129
+ }
130
+ }