123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- import { Injectable } from '@angular/core';
- import { Capacitor } from '@capacitor/core';
- import { Filesystem, Directory } from '@capacitor/filesystem';
- @Injectable({
- providedIn: 'root'
- })
- export class AudioService {
- private audioContext!: AudioContext;
- private mediaRecorder!: MediaRecorder;
- private audioChunks: Blob[] = [];
- private audioAnalyser!: AnalyserNode;
- private microphone!: MediaStreamAudioSourceNode;
- private scriptProcessor!: ScriptProcessorNode;
- private volumeCallback!: (volume: number) => void;
- private isRecording = false;
- constructor() {}
- initAudioContext() {
- if (!this.audioContext) {
- this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
- }
- }
- async startRecording(volumeCallback: (volume: number) => void): Promise<void> {
- this.volumeCallback = volumeCallback;
- this.audioChunks = [];
-
- try {
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
- this.mediaRecorder = new MediaRecorder(stream);
-
- // 设置音频分析
- this.setupAudioAnalysis(stream);
-
- this.mediaRecorder.ondataavailable = (event) => {
- this.audioChunks.push(event.data);
- };
-
- this.mediaRecorder.start(100); // 每100ms收集一次数据
- this.isRecording = true;
- } catch (error) {
- console.error('无法访问麦克风:', error);
- throw error;
- }
- }
- private setupAudioAnalysis(stream: MediaStream) {
- this.microphone = this.audioContext.createMediaStreamSource(stream);
- this.audioAnalyser = this.audioContext.createAnalyser();
- this.audioAnalyser.fftSize = 256;
-
- this.scriptProcessor = this.audioContext.createScriptProcessor(2048, 1, 1);
-
- this.microphone.connect(this.audioAnalyser);
- this.audioAnalyser.connect(this.scriptProcessor);
- this.scriptProcessor.connect(this.audioContext.destination);
-
- const that = this;
- this.scriptProcessor.onaudioprocess = function() {
- if (!that.isRecording) return;
-
- const array = new Uint8Array(that.audioAnalyser.frequencyBinCount);
- that.audioAnalyser.getByteFrequencyData(array);
-
- let values = 0;
- const length = array.length;
-
- for (let i = 0; i < length; i++) {
- values += array[i];
- }
-
- const average = values / length;
- that.volumeCallback(average);
- };
- }
- async stopRecording(): Promise<Blob> {
- return new Promise((resolve) => {
- this.mediaRecorder.onstop = () => {
- const audioBlob = new Blob(this.audioChunks, { type: 'audio/wav' });
- resolve(audioBlob);
- };
-
- this.mediaRecorder.stop();
- this.cleanupAudioNodes();
- this.isRecording = false;
- });
- }
- getRecordedChunks(): Blob[] {
- return this.audioChunks;
- }
- analyzeAudio(audioChunks: Blob[], callback: (dataArray: Uint8Array) => void) {
- const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
- const fileReader = new FileReader();
-
- fileReader.onload = () => {
- const arrayBuffer = fileReader.result as ArrayBuffer;
- this.audioContext.decodeAudioData(arrayBuffer).then((audioBuffer) => {
- const offlineContext = new OfflineAudioContext(
- audioBuffer.numberOfChannels,
- audioBuffer.length,
- audioBuffer.sampleRate
- );
-
- const source = offlineContext.createBufferSource();
- source.buffer = audioBuffer;
-
- const analyser = offlineContext.createAnalyser();
- analyser.fftSize = 2048;
-
- source.connect(analyser);
- analyser.connect(offlineContext.destination);
-
- source.start(0);
-
- offlineContext.startRendering().then(() => {
- const bufferLength = analyser.frequencyBinCount;
- const dataArray = new Uint8Array(bufferLength);
- analyser.getByteTimeDomainData(dataArray);
- callback(dataArray);
- });
- });
- };
-
- fileReader.readAsArrayBuffer(audioBlob);
- }
- private cleanupAudioNodes() {
- if (this.scriptProcessor) {
- this.scriptProcessor.disconnect();
- this.scriptProcessor.onaudioprocess = null;
- }
-
- if (this.microphone) {
- this.microphone.disconnect();
- }
-
- if (this.mediaRecorder && this.mediaRecorder.stream) {
- this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
- }
- }
- }
|