Skip to content

Commit

Permalink
fix detect changes
Browse files Browse the repository at this point in the history
  • Loading branch information
cichy380 committed Jan 20, 2024
1 parent a2b4cdd commit c302863
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 82 deletions.
4 changes: 3 additions & 1 deletion .idea/speech-to-text.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
<button *ngIf="isConverting"
class="flex justify-center items-center w-full h-14 font-semibold text-sm shadow rounded-md text-white bg-blue-500 transition ease-in-out duration-150 cursor-not-allowed"
disabled>
<app-spinner-ico [size]="24" [color]="'white'" class="mr-2"></app-spinner-ico>
<app-spinner-ico [size]="24" [color]="'white'" class="animate-spin mr-2"></app-spinner-ico>
Processing...
</button>
</footer>
Expand Down
64 changes: 35 additions & 29 deletions frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -11,52 +11,58 @@ 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;

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();
});
}

}
65 changes: 15 additions & 50 deletions frontend/src/app/audio-recording.service.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';


@Injectable({
providedIn: 'root'
})
export class AudioRecordingService {

public audioBlob$: Subject<Blob> = 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;
private readonly silenceDuration = 1000;
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);
Expand All @@ -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;
Expand All @@ -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<Blob>((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) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/spinner-icon/spinner-icon.component.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c302863

Please sign in to comment.