【问题标题】:Audio File to Text Using SAPI or Equally Capable SR使用 SAPI 或同等能力 SR 的音频文件到文本
【发布时间】:2016-10-09 12:01:53
【问题描述】:

首先让我解释一下我的目标。我正在努力的目标是提供一个输入 .wav 文件,将其发送到某种语音识别 API,并返回一个带有转录的文本文件。我想到的应用程序非常简单。我不需要解析它的语法或标点符号。它可以返回一个又大又长的句子——这很好。我会将每个转录的单词视为文本文件(.tsv 或 .csv 格式)中的观察结果

但是,我确实需要的一点数据(很棘手,因为我审查过的所有 3rd 方音频转录服务中的 95% 都不向用户提供此类数据)是 SR 猜测的每个单词的 [0.00 - 1.00] 置信度得分。我想将该数据存储在文本文件的新列中,其中包含 .tsv 或 .csv 格式的转录文本。

就是这样。这就是我的目标。看来我的目标是可能的:这是一位专家在相关帖子中的引述:

Convert Audio(Wav file) to Text using SAPI?

SAPI 当然可以做你想做的事。从进程内识别器开始, 将您的音频连接为文件流,设置听写模式,然后关闭 你去吧。

这里是 .wav 转录置信度分数的相关文档:

https://msdn.microsoft.com/en-us/library/jj127911.aspx

https://msdn.microsoft.com/en-us/library/microsoft.speech.recognition.recognizedwordunit.confidence(v=office.14).aspx

每个人都说得这么简单,但现在让我解释一下问题;为什么我要发布一个问题。问题是,对我来说,我的目标遥不可及,因为我对 c++ 或 COM 几乎一无所知。我认为 SAPI 是日常 Windows 体验的一部分,并且具有专用、友好的用户界面。因此,我对这个程序的研究越多,就越感到震惊。不过我还是认为原则上这是一件很简单的事情,所以我很乐观。

我有 Python 知识和一点 JS 知识。我知道 Python 具有其他语言的代码魔法,所以我确信 Python 可以通过这种方式与 SAPI 交互,但由于我不懂 c++,我认为这不会让我变得更好。

所以重申一下,尽管技能不匹配,我仍然偏爱 SAPI,因为所有用户友好的替代品,如 Dragon、Nuance、Chrome 插件等,都不能提供我需要的数据粒度。

现在让我进入我的问题的核心:

  1. 有人可以对我的“目标”的难度进行评估吗?可以在单个 .bat 文件中完成吗?示例代码将不胜感激。

【问题讨论】:

  • 这是一个相当开放的问题,我很乐意有任何意见,无论大小。请记住,我不是世界上最熟练的程序员。所以我可能无法掌握所有这些特定领域的术语,但我正在尽力而为。

标签: c++ com speech-recognition sapi


【解决方案1】:

这可能是不言而喻的,但我认为如果你对 C 作为一种语言没有很好的处理能力,你会发现很难使用 SAPI 的 C 接口。我写了一个程序,几乎完全按照你前段时间所说的来测试这个概念。首先是代码转储:

#include "dirent.h"
#include <iostream>
#include <string>
#include <sapi.h>
#include <sphelper.h>

int main(int argc, char* argv[]){

    DIR *dir;
    struct dirent* entry;
    struct stat* statbuf;
    ::CoInitialize(NULL);
    if((dir = opendir(".")) != NULL){
        while((entry = readdir(dir)) != NULL){
            char extCheck[260];
            strcpy(extCheck, entry->d_name);
            if(strlen(extCheck) > 4 && !strcmp(strlwr(extCheck) + strlen(extCheck)-4, ".wav")){
                //printf("%s\n",entry->d_name);
                //1. Find the wav files
                //2. Check the wavs to make sure they're the correct format
                //3. Output any errors to the error log
                //4. Produce the text files for the wavs
                //5. Cleanup and exit
                FILE* fp;
                std::string fileName = std::string(entry->d_name,entry->d_name + strlen(entry->d_name)-4);
                fileName += ".txt";
                fp = fopen(fileName.c_str(), "w+");
                HRESULT hr = S_OK;
                CComPtr<ISpStream> cpInputStream;
                CComPtr<ISpRecognizer> cpRecognizer;
                CComPtr<ISpRecoContext> cpRecoContext;
                CComPtr<ISpRecoGrammar> cpRecoGrammar;
                CSpStreamFormat sInputFormat;
                hr = cpRecognizer.CoCreateInstance(CLSID_SpInprocRecognizer);
                hr = cpInputStream.CoCreateInstance(CLSID_SpStream);
                hr = sInputFormat.AssignFormat(SPSF_16kHz16BitStereo);
                std::string sInputFileName = entry->d_name;
                std::wstring wInputFileName = std::wstring(sInputFileName.begin(), sInputFileName.end());
                hr = cpInputStream->BindToFile(wInputFileName.c_str(), SPFM_OPEN_READONLY, &sInputFormat.FormatId(), sInputFormat.WaveFormatExPtr(), SPFEI_ALL_EVENTS);
                hr = cpRecognizer->SetInput(cpInputStream, TRUE);
                hr = cpRecognizer->CreateRecoContext(&cpRecoContext);
                hr = cpRecoContext->CreateGrammar(NULL, &cpRecoGrammar);
                hr = cpRecoGrammar->LoadDictation(NULL,SPLO_STATIC);

                hr = cpRecoContext->SetNotifyWin32Event();
                auto hEvent = cpRecoContext->GetNotifyEventHandle();
                hr = cpRecoContext->SetInterest(SPFEI(SPEI_RECOGNITION) | SPFEI(SPEI_END_SR_STREAM), SPFEI(SPEI_RECOGNITION) | SPFEI(SPEI_END_SR_STREAM));
                hr = cpRecoGrammar->SetDictationState(SPRS_ACTIVE);
                BOOL fEndStreamReached = FALSE;
                unsigned int timeOut = 0;
                //WaitForSingleObject(hEvent, INFINITE);
                while (!fEndStreamReached && S_OK == cpRecoContext->WaitForNotifyEvent(INFINITE)){
                    CSpEvent spEvent;

                     while (!fEndStreamReached && S_OK == spEvent.GetFrom(cpRecoContext)){

                        switch (spEvent.eEventId){

                            case SPEI_RECOGNITION:
                                {
                                    auto pPhrase = spEvent.RecoResult();
                                    SPPHRASE *phrase = nullptr;// new SPPHRASE();
                                    LPWSTR* text = new LPWSTR(L"");
                                    pPhrase->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, text, NULL);
                                    pPhrase->GetPhrase(&phrase);

                                    if(phrase != NULL && phrase->pElements != NULL) {
                                        std::wstring wRuleName = L"";

                                        if(nullptr != phrase && phrase->Rule.pszName != NULL) {
                                            wRuleName = phrase->Rule.pszName;
                                        }

                                        std::wstring recognizedText = L"";
                                        bool firstWord = true;
                                        for(ULONG i = 0; i < (ULONG)phrase->Rule.ulCountOfElements; ++i) {

                                            if(phrase->pElements[i].pszDisplayText != NULL) {

                                                std::wstring outString = phrase->pElements[i].pszDisplayText;
                                                std::string soutString = std::string(outString.begin(), outString.end());
                                                if(!firstWord){
                                                    soutString = " " + soutString;
                                                    firstWord = false;
                                                }
                                                soutString = soutString + " ";
                                                fputs(soutString.c_str(),fp);
                                                /*if(recognizedText != L"") {
                                                    recognizedText += L" " + outString;
                                                } else {
                                                    recognizedText += outString;
                                                }*/
                                            }
                                        }

                                    }
                                    delete[] text;

                                    break;
                                }

                            case SPEI_END_SR_STREAM:
                                {
                                    fEndStreamReached = TRUE;
                                    break;
                                }

                        }

                        // clear any event data/object references
                        spEvent.Clear();
                    }
                }

                hr = cpRecoGrammar->SetDictationState(SPRS_INACTIVE);
                hr = cpRecoGrammar->UnloadDictation();
                hr = cpInputStream->Close();

                fclose(fp);
            }
        }
        closedir(dir);
    } else {
        perror("Error opening directory");
    }

    ::CoUninitialize();

    std::printf("Press any key to continue...");
    std::getchar();
    return 0;
}

我已经很久没有运行它了,但是你必须得到 dirent.h 才能让它工作。我在玩那个图书馆只是为了尝试一下。

使用提供的代码,您可能会开始查看在识别步骤中生成了哪些置信度值。如果您愿意,您还可以调整它以从批处理文件运行。

我遇到的问题如下:

  1. 准确性是个问题,为了改进它,我必须训练识别器,这将需要比我更多的时间。
  2. 我发现直接翻译成文本并不是我真正想要的。事实证明,音素数据更为重要。这样您就可以形成自己的置信度方案,并针对您的应用开发自己的替代方案。
  3. Window 的识别器虽然不错,但不会识别它不知道的单词。您必须弄清楚如何将您的词汇添加到 Windows 的语音识别器词典中。

话虽如此,使用现有的 Windows 桌面语音识别器并非易事。我会看看一些现有的 API。如果您不仅限于客户端应用程序,那么您最好研究一下其他 API。

【讨论】:

  • 你所有的点都被拿走了。感谢您展示 SAPI 的功能及其局限性。这正是我评估我的 SR 项目未来所需要的。
  • 哇,我刚刚在 VS2019 下检查了这个 C/C++ 代码,它按原样工作! (有一些小的改动)。准确度似乎相当不错,我用“Microsoft David”语音在 TTS 模式下测试了一个用 SAPI 创建的 WAV 文件。问题,在上面的代码中应该做哪些改变来听现场听写?此外,它使用它找到的第一个 SR。如何选择另一个 SR ? (我安装了 3 种语言)。谢谢!
  • 它适用于 WAV 文件。但如果我使用 MP3 文件,hr = cpInputStream-&gt;BindToFile(wInputFileName.c_str(), SPFM_OPEN_READONLY, &amp;sInputFormat.FormatId(), sInputFormat.WaveFormatExPtr(), SPFEI_ALL_EVENTS); 行将返回 The Parameter is incorrect。问题是,我可以使用 MP3 文件作为 SAPI 的输入吗?如果是,我如何确定调用hr = sInputFormat.AssignFormat(SPSF_16kHz16BitStereo) 的正确格式,因为SPSF_16kHz16BitStereo 肯定不正确,我认为我们不应该对其进行硬编码。
【解决方案2】:

老实说,鉴于您在问题中描述的方法,这相当困难。现有的 SAPI 引擎要么根本不进行听写操作(例如,通过 Microsoft.Speech.Recognition 获得的“服务器”引擎),要么需要经过培训才能学习给定语音的细节(例如,“桌面" 可通过System.Speech.Recognition 获得引擎)。

Windows 运行时识别器 (Windows.Media.SpeechRecognition) 支持听写并提供置信度值,但不支持从流中识别。

对于您描述的方法,我将使用 Bing Speech API,因为它通过 REST API 提供了您想要的置信度值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    • 2013-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    相关资源
    最近更新 更多