From c302863ebcf1f83be2db3a8ea7a17cd6408ad88a Mon Sep 17 00:00:00 2001 From: cichy380 Date: Sat, 20 Jan 2024 20:47:16 +0100 Subject: [PATCH] fix detect changes --- .idea/speech-to-text.iml | 4 +- frontend/src/app/app.component.html | 2 +- frontend/src/app/app.component.ts | 64 +++++++++--------- frontend/src/app/audio-recording.service.ts | 65 +++++-------------- .../spinner-icon/spinner-icon.component.svg | 2 +- 5 files changed, 55 insertions(+), 82 deletions(-) diff --git a/.idea/speech-to-text.iml b/.idea/speech-to-text.iml index d6ebd48..62a332f 100644 --- a/.idea/speech-to-text.iml +++ b/.idea/speech-to-text.iml @@ -2,7 +2,9 @@ - + + + diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 1ed706b..2f68464 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -46,7 +46,7 @@ diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 8d6990e..26c7245 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component } from '@angular/core'; +import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; @@ -11,12 +11,12 @@ import { SpinnerIconComponent } from './spinner-icon/spinner-icon.component'; @Component({ selector: 'app-root', standalone: true, - imports: [CommonModule, HttpClientModule, MicIconComponent, SpinnerIconComponent], + imports: [CommonModule, HttpClientModule, MicIconComponent, SpinnerIconComponent], providers: [AudioProcessingService, AudioRecordingService], templateUrl: './app.component.html', styleUrl: './app.component.css' }) -export class AppComponent { +export class AppComponent implements OnInit { isRecording = false; isConverting = false; @@ -24,39 +24,45 @@ export class AppComponent { text = ''; constructor( - private readonly audioRecordingService: AudioRecordingService, - private readonly audioProcessingService: AudioProcessingService, - private readonly changeDetectorRef: ChangeDetectorRef, + private readonly audioRecordingService: AudioRecordingService, + private readonly audioProcessingService: AudioProcessingService, + private readonly changeDetectorRef: ChangeDetectorRef, ) { } + ngOnInit() { + this.observeAudioBlob(); + } + startRecording() { - this.text = ''; this.isRecording = true; - this.audioRecordingService.startRecording(audioBlob => { - this.isRecording = false; - this.isConverting = true; - this.changeDetectorRef.detectChanges(); - this.audioProcessingService.sendAudio(audioBlob) - .subscribe(transcription => { - this.isConverting = false; - this.text = transcription; - this.changeDetectorRef.detectChanges(); - }); - }); + this.isConverting = false; + this.text = ''; + this.changeDetectorRef.detectChanges(); + this.audioRecordingService.startRecording(); + } + + stopRecording() { + this.audioRecordingService.stopRecording(); + } + + private observeAudioBlob() { + this.audioRecordingService.audioBlob$ + .subscribe(audioBlob => { + this.isRecording = false; + this.isConverting = true; + this.changeDetectorRef.detectChanges(); + this.sendAudioForTranscription(audioBlob); + }); } - async stopRecording() { - this.isRecording = false; - this.isConverting = true; - this.audioRecordingService.stopRecording() - .then(audioBlob => { - this.audioProcessingService.sendAudio(audioBlob) - .subscribe(transcription => { - this.isConverting = false; - this.text = transcription; - }); - }); + private sendAudioForTranscription(audio: Blob) { + this.audioProcessingService.sendAudio(audio) + .subscribe(transcription => { + this.isConverting = false; + this.text = transcription; + this.changeDetectorRef.detectChanges(); + }); } } diff --git a/frontend/src/app/audio-recording.service.ts b/frontend/src/app/audio-recording.service.ts index de3cd00..9959cf1 100644 --- a/frontend/src/app/audio-recording.service.ts +++ b/frontend/src/app/audio-recording.service.ts @@ -1,14 +1,17 @@ import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + @Injectable({ providedIn: 'root' }) export class AudioRecordingService { + public audioBlob$: Subject = new Subject(); + private mediaRecorder!: MediaRecorder; private audioContext!: AudioContext; private audioAnalyser!: AnalyserNode; - private audioWorkletNode!: AudioWorkletNode; private scriptNode!: AudioNode; private recordedChunks: Blob[] = []; private readonly silenceThreshold = 0.2; @@ -16,13 +19,13 @@ export class AudioRecordingService { private silenceStartTime: number | null = null; private stream!: MediaStream; - startRecording(stopRecordingCallback: (audioBlob: Blob) => void) { + startRecording() { this.audioContext = new AudioContext(); this.audioAnalyser = this.audioContext.createAnalyser(); this.scriptNode = this.audioContext.createScriptProcessor(4096, 1, 1); this.silenceStartTime = null; - navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { + navigator.mediaDevices.getUserMedia({audio: true}).then(stream => { this.stream = stream; const sourceNode = this.audioContext.createMediaStreamSource(stream); sourceNode.connect(this.audioAnalyser); @@ -40,7 +43,7 @@ export class AudioRecordingService { if (this.silenceStartTime === null) { this.silenceStartTime = Date.now(); } else if (Date.now() - this.silenceStartTime > this.silenceDuration) { - this.stopRecording().then(audioBlob => stopRecordingCallback(audioBlob)); + this.stopRecording(); } } else { this.silenceStartTime = null; @@ -55,53 +58,15 @@ export class AudioRecordingService { }); } - // startRecording(stopRecordingCallback: (audioBlob: Blob) => void) { - // this.audioContext = new AudioContext(); - // this.audioContext.audioWorklet.addModule('assets/audio-worklet-processor.js').then(() => { - // this.audioAnalyser = this.audioContext.createAnalyser(); - // this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'audioWorkletProcessor'); - // - // navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { - // const sourceNode = this.audioContext.createMediaStreamSource(stream); - // sourceNode.connect(this.audioAnalyser); - // this.audioAnalyser.connect(this.audioWorkletNode); - // // this.audioWorkletNode.connect(this.audioContext.destination); - // - // this.audioWorkletNode.port.onmessage = (event) => { - // const average = this.getAverageVolume(event.data.volumeArray); - // - // if (average < this.silenceThreshold) { - // if (this.silenceStartTime === null) { - // this.silenceStartTime = Date.now(); - // } else if (Date.now() - this.silenceStartTime > this.silenceDuration) { - // // this.stopRecording(); - // this.stopRecording().then(audioBlob => stopRecordingCallback(audioBlob)); - // } - // } else { - // this.silenceStartTime = null; - // } - // }; - // - // this.mediaRecorder = new MediaRecorder(stream); - // this.mediaRecorder.start(); - // this.mediaRecorder.ondataavailable = (event) => { - // this.recordedChunks.push(event.data); - // }; - // }); - // }); - // } - stopRecording() { - return new Promise((resolve, _reject) => { - this.mediaRecorder.onstop = async () => { - const blob = new Blob(this.recordedChunks, { type: 'audio/wav' }); - this.recordedChunks = []; - await this.audioContext.close(); - this.stream.getTracks().forEach(track => track.stop()); - resolve(blob); - }; - this.mediaRecorder.stop(); - }); + this.mediaRecorder.onstop = async () => { + const blob = new Blob(this.recordedChunks, {type: 'audio/wav'}); + this.recordedChunks = []; + await this.audioContext.close(); + this.stream.getTracks().forEach(track => track.stop()); + this.audioBlob$.next(blob); + }; + this.mediaRecorder.stop(); } private getAverageVolume(array: Uint8Array) { diff --git a/frontend/src/app/spinner-icon/spinner-icon.component.svg b/frontend/src/app/spinner-icon/spinner-icon.component.svg index 3b2a7ae..c87f02b 100644 --- a/frontend/src/app/spinner-icon/spinner-icon.component.svg +++ b/frontend/src/app/spinner-icon/spinner-icon.component.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file