«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Ввод аудио в текст через Google Речь в текст

Ввод аудио в текст через Google Речь в текст

Опубликовано 1 ноября 2024 г.
Просматривать:556

Audio to Text Input via Google Speech to Text

В этой статье мы рассмотрим следующие темы

  1. navigator.mediaDevices.getUserMedia API браузера
  2. API преобразования речи в текст

мы начнем с создания хука реагирования, который будет выполнять все такие операции, как startRecording, stopRecording, создание Audio Blob, обработку ошибок и т. д.

Есть еще несколько вещей, о которых нужно позаботиться, прежде чем мы перейдем к сути дела.

  1. Минимальный децибел, выше которого мы будем считать диалог входными данными, например -35 дБ (просто случайное число)
  2. Как долго должна быть пауза, указывающая на то, что пользователь остановил ввод, например 2000 мс
const VOICE_MIN_DECIBELS = -35
const DELAY_BETWEEN_DIALOGUE = 2000

Давайте назовем наш хук useAudioInput.ts, мы будем использовать API браузера, например navigator.mediaDevices.getUserMedia, MediaRecorder и AudioContext. 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([]);
    const [isRecording, setIsRecording] = useState(false);
    const mediaRecorder = useRef(null);
    const [error, setError] = useState(null);
    let requestId: number;
    let timer: ReturnType;

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

В приведенном выше коде мы будем использовать mediaChunks в качестве переменной для хранения входного объекта и mediaRecorder для создания экземпляра нового MediaRecorder, который принимает поток в качестве входных данных из navigator.mediaDevices.getUserMedia. Далее давайте разберемся со случаями, когда 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?.();

с приведенным выше кодом мы почти закончили работу с перехватчиком, единственное, что еще предстоит сделать, это определить, перестал ли пользователь говорить или нет, мы будем использовать DELAY_BETWEEN_DIALOGUE в качестве времени ожидания, если нет ввода для 2 секунд мы будем считать, что пользователь прекратил говорить и перейдет к конечной точке речи в текст.

...
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  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 в текстовом 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 Speech to Text, мы также можем отправлять BLOB-URI вместо контента, единственным изменением будет полезная нагрузка

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

Код, относящийся к статье, присутствует в Github.

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/shubhadip/audio-to-text-input-via-google-speech-to-text-4ob0?1. Если есть какие-либо нарушения, свяжитесь с [email protected]. удалить его
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3