Распознавание речи с помощью Microsoft Cognitive Speech API и аудиомотока в режиме реального времени без микрофона - PullRequest
0 голосов
/ 08 ноября 2018

Задача

Мой проект состоит из настольного приложения, которое записывает аудио в режиме реального времени, для которого я намерен получать отзывы распознавания в реальном времени от API. С микрофоном реализация в режиме реального времени с использованием нового API речи для текста тривиальна, и мой сценарий отличается от сценария только в том смысле, что мои данные записываются в MemoryStream объект.

Поддержка API

В этой статье объясняется, как реализовать API Recognizer ( link ) с пользовательскими аудиопотоками , что неизменно требует реализации абстрактного класса PullAudioInputStream ( ссылка ) для создания необходимого объекта AudioConfig с использованием метода CreatePullStream ( ссылка ). Другими словами, для достижения того, что мне требуется, должен быть реализован интерфейс обратного вызова.

Попытка реализации

Поскольку мои данные записываются в MemoryStream (а библиотека, которую я использую, будет записывать только файлы или объекты Stream), в приведенном ниже коде я просто копирую буфер в реализованный класс ( небрежно, возможно? ) устранение расхождений в сигнатурах методов.

class AudioInputCallback : PullAudioInputStreamCallback
{
    private readonly MemoryStream memoryStream;

    public AudioInputCallback(MemoryStream stream)
    {
        this.memoryStream = stream;
    }

    public override int Read(byte[] dataBuffer, uint size)
    {
        return this.Read(dataBuffer, 0, dataBuffer.Length);
    }

    private int Read(byte[] buffer, int offset, int count)
    {
        return memoryStream.Read(buffer, offset, count);
    }

    public override void Close()
    {
        memoryStream.Close();
        base.Close();
    }

}

Реализация Recognizer выглядит следующим образом:

private SpeechRecognizer CreateMicrosoftSpeechRecognizer(MemoryStream memoryStream)
{
    var recognizerConfig = SpeechConfig.FromSubscription(SubscriptionKey, @"westus");
    recognizerConfig.SpeechRecognitionLanguage =
        _programInfo.CurrentSourceCulture.TwoLetterISOLanguageName;

    // Constants are used as constructor params)
    var format = AudioStreamFormat.GetWaveFormatPCM(
        samplesPerSecond: SampleRate, bitsPerSample: BitsPerSample, channels: Channels);

    // Implementation of PullAudioInputStreamCallback
    var callback = new AudioInputCallback(memoryStream);
    AudioConfig audioConfig = AudioConfig.FromStreamInput(callback, format);

    //Actual recognizer is created with the required objects
    SpeechRecognizer recognizer = new SpeechRecognizer(recognizerConfig, audioConfig);

    // Event subscriptions. Most handlers are implemented for debugging purposes only.
    // A log window outputs the feedback from the event handlers.
    recognizer.Recognized += MsRecognizer_Recognized;
    recognizer.Recognizing += MsRecognizer_Recognizing;
    recognizer.Canceled += MsRecognizer_Canceled;
    recognizer.SpeechStartDetected += MsRecognizer_SpeechStartDetected;
    recognizer.SpeechEndDetected += MsRecognizer_SpeechEndDetected;
    recognizer.SessionStopped += MsRecognizer_SessionStopped;
    recognizer.SessionStarted += MsRecognizer_SessionStarted;

    return recognizer;
}

Как данные становятся доступными для распознавателя (с помощью CSCore):

MemoryStream memoryStream = new MemoryStream(_finalSource.WaveFormat.BytesPerSecond / 2);
byte[] buffer = new byte[_finalSource.WaveFormat.BytesPerSecond / 2];

_soundInSource.DataAvailable += (s, e) =>
{
    int read;
    _programInfo.IsDataAvailable = true;

    // Writes to MemoryStream as event fires
    while ((read = _finalSource.Read(buffer, 0, buffer.Length)) > 0)
        memoryStream.Write(buffer, 0, read);
};

// Creates MS recognizer from MemoryStream
_msRecognizer = CreateMicrosoftSpeechRecognizer(memoryStream);

//Initializes loopback capture instance
_soundIn.Start();

await Task.Delay(1000);

// Starts recognition
await _msRecognizer.StartContinuousRecognitionAsync();

Результат

Когда приложение запускается, я не получаю никаких исключений и никаких ответов от API, кроме SessionStarted и SessionStopped, как показано ниже в окне журнала моего приложения.

enter image description here

Я мог бы использовать предложения различных подходов к моей реализации, так как подозреваю, что есть некоторая проблема с синхронизацией при привязке записанного события DataAvailable с фактической отправкой данных в API, что заставляет его преждевременно отбрасывать сеанс. Без подробного отзыва о том, почему мои запросы были неудачными, я могу только догадываться о причине.

1 Ответ

0 голосов
/ 09 ноября 2018

Обратный вызов Read() для PullAudioInputStream должен блокироваться, если нет доступных данных. И Read() возвращает 0, только если поток достигает конца. Затем SDK закроет поток после того, как Read() вернет 0 (найдите ссылку на API doc здесь ).

Однако поведение Read () в C # MemoryStream отличается: возвращается 0, если в буфере нет данных. Вот почему вы видите только события SessionStart и SessionStop, но не события распознавания.

Чтобы это исправить, вам нужно добавить некую синхронизацию между PullAudioInputStream::Read() и MemoryStream::Write(), чтобы убедиться, что PullAudioInputStream::Read() будет ждать, пока MemoryStream::Write() запишет некоторые данные в буфер.

В качестве альтернативы я бы порекомендовал использовать PushAudioInputStream, что позволяет напрямую записывать данные в поток. В вашем случае в случае _soundSource.DataAvailable вместо записи данных в MemoryStream вы можете напрямую записать их в PushAudioInputStream. Вы можете найти образцы для PushAudioInputStream здесь .

Мы обновим документацию, чтобы предоставить лучшие рекомендации по использованию Pull и Push AudioInputStream. Приносим извинения за неудобства.

Спасибо!

...