使用 SAPI 或同等能力的 SR 将音频文件转为文本
Audio File to Text Using SAPI or Equally Capable SR
首先让我解释一下我的目标。我正在努力实现的目标是提供一个输入 .wav 文件,将其发送到某种语音识别 API,然后 return 使用转录文本文件。我想到的应用程序非常简单。我不要求对它进行语法或标点符号解析。它可以 return 一个又大又长的句子——没关系。我会将每个转录的单词视为文本文件(.tsv 或 .csv 格式)中的观察结果
然而,一个棘手的数据(棘手是因为我审查过的所有第 3 方音频转录服务中有 95% 不向用户提供此类数据) 我确实需要的是 SR 猜测的每个单词的 [0.00 - 1.00] 置信度分数。我想将该数据存储在包含 .tsv 或 .csv 格式的转录文本的文本文件的新列中。
就是这样。那是我的目标。看来我的目标是可能的:这是相关 post:
中一位专家的引述
Convert Audio(Wav file) to Text using SAPI?
SAPI can certainly do what you want. Start with an in-proc recognizer,
connect up your audio as a file stream, set dictation mode, and off
you go.
这里是 .wav 转录置信度分数的相关文档:
https://msdn.microsoft.com/en-us/library/jj127911.aspx
大家都说的那么简单,现在我来解释一下问题;为什么我要 post 提问。问题是,对我来说,我的目标遥不可及,因为我对 C++ 或 COM 几乎一无所知。我认为 SAPI 是日常 windows 体验的一部分,并且具有专用、友好的用户界面。因此,我对这个程序的研究越多,我就越担心。不过我还是相信原则上这是一件很简单的事情,所以我很乐观。
我懂 Python 和一点点 JS。我知道 Python 对其他语言有代码魔法,所以我确定 Python 可以通过这种方式与 SAPI 交互,但由于我不懂 c++,所以我不认为那会让我过得更好。
所以重申一下,尽管技能不匹配,但我仍然偏爱 SAPI,因为所有用户友好的替代品,如 Dragon、Nuance、Chrome 插件等,没有提供我需要的数据粒度。
现在让我进入问题的核心:
- 有人可以给我评估一下我的 "goal" 的难度吗?可以在单个 .bat 文件中完成吗?示例代码将不胜感激。
老实说,鉴于您在问题中描述的方法,这相当困难。现有的 SAPI 引擎要么根本不执行听写 (例如,"server" 引擎可通过 Microsoft.Speech.Recognition) or require training to learn the particulars of a given voice (e.g., the "desktop" engine available via System.Speech.Recognition 获得)。
Windows 运行时识别器 (Windows.Media.SpeechRecognition) 支持听写并提供置信度值,但不支持从流中识别。
根据您描述的方法,我会使用 Bing Speech API,因为它通过 REST API.
提供您想要的置信度值
这可能不言而喻,但我认为如果您对 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 才能正常工作。除了尝试一下之外,我一直在玩那个库。
使用提供的代码,您可能可以开始查看在识别步骤中生成的置信度值。如果需要,您也可以从批处理文件中将其调整为 运行。
我遇到的问题如下:
- 准确性是个问题,为了提高它,我必须训练识别器,这将需要比我多得多的时间。
- 我发现直接翻译成文字并不是我真正想要的。事实证明,音素数据更为重要。有了它,您可以形成自己的置信度方案,并针对您的应用开发自己的替代方案。
- Window 的识别器虽然不错,但不会识别它不知道的单词。您必须弄清楚如何将您的词汇添加到 windows' 语音识别词典。
话虽如此,使用常用 windows 桌面语音识别器并非易事。我会看看那里的一些现有 API。如果您不仅限于客户端应用程序,那么您最好研究一下其他 API。
首先让我解释一下我的目标。我正在努力实现的目标是提供一个输入 .wav 文件,将其发送到某种语音识别 API,然后 return 使用转录文本文件。我想到的应用程序非常简单。我不要求对它进行语法或标点符号解析。它可以 return 一个又大又长的句子——没关系。我会将每个转录的单词视为文本文件(.tsv 或 .csv 格式)中的观察结果
然而,一个棘手的数据(棘手是因为我审查过的所有第 3 方音频转录服务中有 95% 不向用户提供此类数据) 我确实需要的是 SR 猜测的每个单词的 [0.00 - 1.00] 置信度分数。我想将该数据存储在包含 .tsv 或 .csv 格式的转录文本的文本文件的新列中。
就是这样。那是我的目标。看来我的目标是可能的:这是相关 post:
中一位专家的引述Convert Audio(Wav file) to Text using SAPI?
SAPI can certainly do what you want. Start with an in-proc recognizer, connect up your audio as a file stream, set dictation mode, and off you go.
这里是 .wav 转录置信度分数的相关文档:
https://msdn.microsoft.com/en-us/library/jj127911.aspx
大家都说的那么简单,现在我来解释一下问题;为什么我要 post 提问。问题是,对我来说,我的目标遥不可及,因为我对 C++ 或 COM 几乎一无所知。我认为 SAPI 是日常 windows 体验的一部分,并且具有专用、友好的用户界面。因此,我对这个程序的研究越多,我就越担心。不过我还是相信原则上这是一件很简单的事情,所以我很乐观。
我懂 Python 和一点点 JS。我知道 Python 对其他语言有代码魔法,所以我确定 Python 可以通过这种方式与 SAPI 交互,但由于我不懂 c++,所以我不认为那会让我过得更好。
所以重申一下,尽管技能不匹配,但我仍然偏爱 SAPI,因为所有用户友好的替代品,如 Dragon、Nuance、Chrome 插件等,没有提供我需要的数据粒度。
现在让我进入问题的核心:
- 有人可以给我评估一下我的 "goal" 的难度吗?可以在单个 .bat 文件中完成吗?示例代码将不胜感激。
老实说,鉴于您在问题中描述的方法,这相当困难。现有的 SAPI 引擎要么根本不执行听写 (例如,"server" 引擎可通过 Microsoft.Speech.Recognition) or require training to learn the particulars of a given voice (e.g., the "desktop" engine available via System.Speech.Recognition 获得)。
Windows 运行时识别器 (Windows.Media.SpeechRecognition) 支持听写并提供置信度值,但不支持从流中识别。
根据您描述的方法,我会使用 Bing Speech API,因为它通过 REST API.
提供您想要的置信度值这可能不言而喻,但我认为如果您对 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 才能正常工作。除了尝试一下之外,我一直在玩那个库。
使用提供的代码,您可能可以开始查看在识别步骤中生成的置信度值。如果需要,您也可以从批处理文件中将其调整为 运行。
我遇到的问题如下:
- 准确性是个问题,为了提高它,我必须训练识别器,这将需要比我多得多的时间。
- 我发现直接翻译成文字并不是我真正想要的。事实证明,音素数据更为重要。有了它,您可以形成自己的置信度方案,并针对您的应用开发自己的替代方案。
- Window 的识别器虽然不错,但不会识别它不知道的单词。您必须弄清楚如何将您的词汇添加到 windows' 语音识别词典。
话虽如此,使用常用 windows 桌面语音识别器并非易事。我会看看那里的一些现有 API。如果您不仅限于客户端应用程序,那么您最好研究一下其他 API。