Доброго времени суток!
При разработке веб-сайта мне понадобилась функция потоковой распознавания речи. Хочу использовать Yandex speechKit для распознавания русской речи.
На фронте использую реакт, для записи голоса использую
react-mic.
На бэке использую
node.js + express.js
Я так понял, что для потоковой распознавания речи нужны вебсокеты. Хоть я и не силен в этой технологии, но кое-как я реализовал соединение с сервером, но все же был бы признателен за более правильную реализацию отправки маленьких 0,5 секундных кусочков аудио на сервер) (но это второстепенная проблема)
вот моя реализация:
import React, { useState, useEffect } from 'react';
import { ReactMic } from 'react-mic';
import io from 'socket.io-client';
const AudioRecorder = () => {
const [record, setRecord] = useState(false);
const [socket, setSocket] = useState(null);
const [transcript, setTranscript] = useState('');
useEffect(() => {
if (socket) {
socket.on('recognitionResult', (data) => {
setTranscript((prev) => `${prev} ${data.alternatives[0].text}`);
});
socket.on('partialResult', (data) => {
console.log('Partial result:', data);
});
socket.on('recognitionError', (error) => {
console.error(error);
});
}
return () => {
if (socket) {
socket.off('recognitionResult');
socket.off('partialResult');
socket.off('recognitionError');
}
};
}, [socket]);
const startRecording = () => {
const socketInstance = io('http://localhost:3000');
setSocket(socketInstance);
setRecord(true);
setTranscript(''); // Clear transcript when starting a new recording
};
const stopRecording = () => {
setRecord(false);
if (socket) {
console.log('Emitting endAudioStream');
socket.emit('endAudioStream');
socket.disconnect();
}
};
const onData = (recordedChunk) => {
if (socket) {
socket.emit('audioStream', recordedChunk);
}
};
return (
<div>
<ReactMic
record={record}
className="sound-wave"
onStop={stopRecording}
onData={onData}
mimeType="audio/webm"
strokeColor="#000000"
backgroundColor="#FF4081"
/>
<button onClick={startRecording} type="button">Start</button>
<button onClick={stopRecording} type="button">Stop</button>
<div>
<h3>Transcript:</h3>
<p>{transcript}</p>
</div>
</div>
);
};
export default AudioRecorder;
Главная проблема на сервере:
Я почитал
документацию яндекса о потоковом распознавании, но к сожалению не нашел ничего дельного, может быть что-то проглядел...
Можете пожалуйста написать скрипт на
node.js + express, который бы принимал эти чанки аудио и корректно отправлял бы их на сервера яндекса для обработки, чтобы когда пользователь закончил свое аудио, мы с максимум задержкой 1-2 секунды уже имели полное текстовое содержание его речи на сервере.
Отправлять распознанный текст на фронт не надо, просто его собирать и по окончанию сделать запрос на chatGPT (с chatGPT я уже написал код)
Код сервера на данный момент, который выводит ошибку
gRPC Error: Error: 12 UNIMPLEMENTED:
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const { Transform } = require('stream');
const cors = require('cors');
const PROTO_PATH = './recognizer.proto';
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const recognizerProto = grpc.loadPackageDefinition(packageDefinition).yandex.cloud.ai.stt.v3;
const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
cors: {
origin: "http://localhost:5173",
methods: ["GET", "POST"]
}
});
app.use(cors());
io.on('connection', (socket) => {
console.log('New client connected');
const client = new recognizerProto.Recognizer('stt.api.cloud.yandex.net:443', grpc.credentials.createSsl());
const metadata = new grpc.Metadata();
metadata.add('authorization', 'Api-Key ЗДЕСЬ МОЙ API КЛЮЧ');
const call = client.RecognizeStreaming(metadata);
call.on('data', (response) => {
console.log('Response received:', response);
if (response.event === 'partial') {
socket.emit('partialResult', response.partial);
} else if (response.event === 'final') {
socket.emit('recognitionResult', response.final);
}
});
call.on('error', (error) => {
console.error('gRPC Error:', error);
socket.emit('recognitionError', error.message);
});
call.on('end', () => {
console.log('Stream ended');
});
// Sending initial session options
call.write({
session_options: {
recognition_model: {
model: 'general',
audio_format: {
container_audio: {
container_audio_type: 'OGG_OPUS'
}
},
text_normalization: {
text_normalization: 'TEXT_NORMALIZATION_ENABLED'
},
language_restriction: {
restriction_type: 'WHITELIST',
language_code: ['ru-RU']
},
audio_processing_type: 'REAL_TIME'
}
}
});
const audioTransform = new Transform({
transform(chunk, encoding, callback) {
call.write({ chunk: { data: chunk } });
callback();
}
});
socket.on('audioStream', (chunk) => {
audioTransform.write(Buffer.from(chunk));
});
socket.on('endAudioStream', () => {
console.log('audio stream end');
audioTransform.end();
call.end();
});
socket.on('disconnect', () => {
console.log('Client disconnected');
audioTransform.end();
call.end();
});
});
const port = 3000;
server.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
P.S. сижу уже с этой задачей неделю, никак не могу решить. ChatGPT гоняет одну и ту же ошибку. Заранее спасибо за помощь!