Heim > Web-Frontend > js-Tutorial > Hauptteil

Audio-zu-Text-Eingabe über Google Speech to Text

Mary-Kate Olsen
Freigeben: 2024-10-20 14:33:29
Original
133 Leute haben es durchsucht

Audio to Text Input via Google Speech to Text

In diesem Artikel werden wir uns mit folgenden Themen befassen

  1. navigator.mediaDevices.getUserMedia-Browser-API
  2. Google Speech to Text API

Wir beginnen mit der Erstellung eines Reaktions-Hooks, der alle Aufgaben erledigt, wie Aufnahme starten, Aufnahme stoppen, Audio-Blob erstellen, Fehlerbehandlung usw.

Es gibt noch ein paar andere Dinge, um die wir uns kümmern müssen, bevor wir uns an die Arbeit machen

  1. Mindestdezibel, oberhalb dessen wir einen Dialog als Eingabe betrachten würden, z. B. -35 dB (nur eine Zufallszahl)
  2. Wie lang sollte die Pause sein, die anzeigt, dass der Benutzer die Eingabe gestoppt hat, z. B. 2000 ms?
const VOICE_MIN_DECIBELS = -35
const DELAY_BETWEEN_DIALOGUE = 2000

Nach dem Login kopieren

Nennen wir unseren Hook useAudioInput.ts. Wir würden die Browser-APIs wie navigator.mediaDevices.getUserMedia, MediaRecorder und AudioContext verwenden. Mithilfe von AudioContext können wir ermitteln, ob das Eingangsaudio höher ist als die Mindestdezibel, die erforderlich ist, damit es als Eingabe betrachtet wird. Daher beginnen wir mit den folgenden Variablen und Requisiten

const defaultConfig = {
    audio: true
};

type Payload = Blob;

type Config = {
    audio: boolean;
    timeSlice?: number
    timeInMillisToStopRecording?: number
    onStop: () => void;
    onDataReceived: (payload: Payload) => void
};

export const useAudioInput = (config: Config = defaultConfig) => {
    const mediaChunks = useRef<Blob[]>([]);
    const [isRecording, setIsRecording] = useState(false);
    const mediaRecorder = useRef<MediaRecorder | null>(null);
    const [error, setError] = useState<Error| null>(null);
    let requestId: number;
    let timer: ReturnType<typeof setTimeout>;

    const createBlob = () => {
      const [chunk] = mediaChunks.current;
      const blobProperty = { type: chunk.type };
      return new Blob(mediaChunks.current, blobProperty)
    }
  ...
}

Nach dem Login kopieren

Im obigen Code würden wir mediaChunks als Variable verwenden, um den Eingabeblob zu speichern, und mediaRecorder, um eine Instanz des neuen MediaRecorder zu haben, der den Stream als Eingabe von navigator.mediaDevices.getUserMedia übernimmt. Als nächstes kümmern wir uns um Fälle, in denen getUserMedia nicht verfügbar ist

...
useEffect(() => {
        if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
            const notAvailable = new Error('Your browser does not support Audio Input')
            setError(notAvailable)
        }

    },[]);
...

Nach dem Login kopieren

Wir beginnen mit dem Schreiben der eigentlichen Funktionalität des Hooks, die aus verschiedenen Funktionen wie setupMediaRecorder, setupAudioContext, onRecordingStart, onRecordingActive, startRecording, stopRecording usw. besteht.

const onRecordingStart = () => mediaChunks.current = [];

const onRecordingActive = useCallback(({data}: BlobEvent) => {
        if(data) {
            mediaChunks.current.push(data);
            config?.onDataReceived?.(createBlob())
        }
    },[config]);

const startTimer = () => {
        timer = setTimeout(() => {
            stopRecording();
        }, config.timeInMillisToStopRecording)
    };

const setupMediaRecorder = ({stream}:{stream: MediaStream}) => {
        mediaRecorder.current = new MediaRecorder(stream)
        mediaRecorder.current.ondataavailable = onRecordingActive
        mediaRecorder.current.onstop = onRecordingStop
        mediaRecorder.current.onstart = onRecordingStart
        mediaRecorder.current.start(config.timeSlice)

    };

 const setupAudioContext = ({stream}:{stream: MediaStream}) => {
        const audioContext = new AudioContext();
        const audioStreamSource = audioContext.createMediaStreamSource(stream);
        const analyser = audioContext.createAnalyser();

        analyser.minDecibels = VOICE_MIN_DECIBELS;

        audioStreamSource.connect(analyser);
        const bufferLength = analyser.frequencyBinCount;
        const domainData = new Uint8Array(bufferLength)

        return {
            domainData,
            bufferLength,
            analyser
        }
    };

const startRecording = async () => {
        setIsRecording(true);

        await navigator.mediaDevices
            .getUserMedia({
                audio: config.audio
            })
            .then((stream) => {
                setupMediaRecorder({stream});
                if(config.timeSlice) {
                    const { domainData, analyser, bufferLength } = setupAudioContext({ stream });
                    startTimer()
                }
            })
            .catch(e => {
                setError(e);
                setIsRecording(false)
            })
    };



    const stopRecording = () => {
        mediaRecorder.current?.stop();

        clearTimeout(timer);
        window.cancelAnimationFrame(requestId);

        setIsRecording(false);
        onRecordingStop()
    };

    const createBlob = () => {
        const [chunk] = mediaChunks.current;
        const blobProperty = { type: chunk.type };
        return new Blob(mediaChunks.current, blobProperty)
    }

    const onRecordingStop = () => config?.onStop?.();

Nach dem Login kopieren

Mit dem obigen Code sind wir mit dem Hook fast fertig. Das einzige, was noch aussteht, ist festzustellen, ob der Benutzer aufgehört hat zu sprechen oder nicht. Wir würden DELAY_BETWEEN_DIALOGUE als die Zeit verwenden, auf die wir warten würden, wenn für 2 keine Eingabe erfolgt Sekunden gehen wir davon aus, dass der Benutzer aufgehört hat zu sprechen und den Sprach-zu-Text-Endpunkt erreicht.

...
const detectSound = ({ 
        recording,
        analyser,
        bufferLength,
        domainData
    }: {
        recording: boolean
        analyser: AnalyserNode
        bufferLength: number
        domainData: Uint8Array
    }) => {
        let lastDetectedTime = performance.now();
        let anySoundDetected = false;

        const compute = () => {
            if (!recording) {
                return;
            }

            const currentTime = performance.now();

            const timeBetweenTwoDialog =
                anySoundDetected === true && currentTime - lastDetectedTime > DELAY_BETWEEN_DIALOGUE;

            if (timeBetweenTwoDialog) {
                stopRecording();

                return;
            }

            analyser.getByteFrequencyData(domainData);

            for (let i = 0; i < bufferLength; i += 1) {
                if (domainData[i] > 0) {
                    anySoundDetected = true;
                    lastDetectedTime = performance.now();
                }
            }

            requestId = window.requestAnimationFrame(compute);
        };

        compute();

    }
...

const startRecording = async () => {
 ... 
  detectSound()
 ... 
}
Nach dem Login kopieren

Im obigen Code verwenden wir requestAnimationFrame, um die Audioeingabe des Benutzers zu erkennen. Damit sind wir mit dem Hook fertig und können nun beginnen, den Hook an verschiedenen Stellen zu verwenden.

z.B.

  const onDataReceived = async (data: BodyInit) => {
    const rawResponse = await fetch('https://backend-endpoint', {
      method: 'POST',
      body: data
    });
    const response = await rawResponse.json();

    setText(response)
  };

  const { isRecording, startRecording, error } = useAudioInput({
    audio: true,
    timeInMillisToStopRecording: 2000,
    timeSlice: 400,
    onDataReceived
  })

Nach dem Login kopieren

Der zweite Teil besteht darin, einen Knotenserver zu verkabeln, der mit Google Speech to Text API kommunizieren kann. Ich habe die Dokumentation angehängt, auf die ich beim Erstellen der Knotenseite verwiesen habe.
https://codelabs.developers.google.com/codelabs/cloud-speech-text-node.

// demo node server which connects with google speech to text api endpoint

const express = require('express');
const cors = require('cors');

const speech = require('@google-cloud/speech');

const client = new speech.SpeechClient();

async function convert(audioBlob) {
  const request = {
    config: {
      encoding: 'WEBM_OPUS', // Ensure this matches the format of the audio being sent
      sampleRateHertz: 48000, // This should match the sample rate of your recording
      languageCode: 'en-US'
    },
    audio: {
      content: audioBlob
    }
  };

  const [response] = await client.recognize(request);

  const transcription = response.results
    .map(result => result.alternatives[0].transcript)
    .join('\n');
  return transcription;
}

const app = express();

app.use(cors())
app.use(express.json());

app.post('/upload', express.raw({ type: '*/*' }), async (req, res) => {
    const audioBlob = req.body;

    const response = await convert(audioBlob);

    res.json(response);
});

app.listen(4000,'0.0.0.0', () => {
  console.log('Example app listening on port 4000!');
});


Nach dem Login kopieren

In diesem Artikel habe ich das Senden von Audioinhalten oder Blobs an den Google Speech to Text-Endpunkt behandelt. Wir können auch eine Blob-URI anstelle von Inhalten senden. Die einzige Änderung ist die Nutzlast

// sending url as part of audio object to speech to text api 
...
audio: {url: audioUrl} or audio: {content: audioBlob}
...
Nach dem Login kopieren

Der Code zum Artikel ist in Github vorhanden.

Das obige ist der detaillierte Inhalt vonAudio-zu-Text-Eingabe über Google Speech to Text. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!