> 웹 프론트엔드 > JS 튜토리얼 > Google Speech to Text를 통한 오디오-텍스트 입력

Google Speech to Text를 통한 오디오-텍스트 입력

Mary-Kate Olsen
풀어 주다: 2024-10-20 14:33:29
원래의
253명이 탐색했습니다.

Audio to Text Input via Google Speech to Text

이 기사에서는 다음 주제를 살펴볼 것입니다

  1. navigator.mediaDevices.getUserMedia 브라우저 API
  2. Google 음성을 텍스트 API로 변환

startRecording, stopRecording, Audio Blob 생성, 오류 처리 등과 같은 모든 작업을 수행하는 반응 후크를 만드는 것부터 시작하겠습니다.

갈고리의 고기에 들어가기 전에 처리해야 할 사항이 몇 가지 있습니다

  1. 대화를 입력으로 간주하는 최소 데시벨(예: -35db(임의의 숫자))
  2. 사용자가 입력을 중지했음을 나타내는 일시 중지 시간은 얼마나 길어야 합니까?(예: 2000ms)
const VOICE_MIN_DECIBELS = -35
const DELAY_BETWEEN_DIALOGUE = 2000

로그인 후 복사

후크 이름을 useAudioInput.ts로 지정하고 navigator.mediaDevices.getUserMedia, MediaRecorder 및 AudioContext와 같은 브라우저 API를 사용하겠습니다. AudioContext는 입력 오디오가 입력으로 간주되는 데 필요한 최소 데시벨보다 높은지 여부를 식별하는 데 도움이 되므로 다음 변수와 소품으로 시작합니다

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)
    }
  ...
}

로그인 후 복사

위 코드에서는 mediaChunks를 변수로 사용하여 입력 blob을 보관하고 mediaRecorder를 사용하여 navigator.mediaDevices.getUserMedia에서 스트림을 입력으로 가져오는 새 MediaRecorder의 인스턴스를 갖습니다. 다음으로 getUserMedia를 사용할 수 없는 경우를 처리해 보겠습니다.

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

    },[]);
...

로그인 후 복사

setupMediaRecorder, setupAudioContext, onRecordingStart, onRecordingActive, startRecording, stopRecording 등과 같은 다양한 기능으로 구성된 후크의 실제 기능을 작성하기 시작합니다.

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?.();

로그인 후 복사

위 코드를 사용하면 후크 작업이 거의 완료되었습니다. 유일하게 보류 중인 작업은 사용자가 말을 멈췄는지 여부를 식별하는 것입니다. 2에 대한 입력이 없는 경우 기다릴 시간으로 DELAY_BETWEEN_DIALOGUE를 사용합니다. 초 동안 사용자가 말하기를 중단하고 텍스트 끝점으로 음성을 전달한다고 가정합니다.

...
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()
 ... 
}
로그인 후 복사

위 코드에서는 requestAnimationFrame을 사용하여 사용자 오디오 입력을 감지합니다. 이로써 후크 작업이 완료되었으며 이제 다양한 위치에서 후크 사용을 시작할 수 있습니다.


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

로그인 후 복사

두 번째 부분은 Google Speech와 통신할 수 있는 노드 서버를 텍스트 API에 연결하는 것입니다. 노드 측을 생성할 때 참조한 문서를 첨부했습니다.
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!');
});


로그인 후 복사

이 기사에서는 오디오 콘텐츠 또는 Blob을 Google 음성으로 텍스트 엔드포인트로 보내는 방법을 다루었습니다. 또한 콘텐츠 대신 Blob URI를 보낼 수도 있습니다. 유일한 변경 사항은 페이로드뿐입니다.

// sending url as part of audio object to speech to text api 
...
audio: {url: audioUrl} or audio: {content: audioBlob}
...
로그인 후 복사

기사와 관련된 코드는 Github에 있습니다.

위 내용은 Google Speech to Text를 통한 오디오-텍스트 입력의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿