Я прочитал несколько статей и разработал поток этого кода на основе описания Эрика Брауна здесь. . Существуют примеры использования существующих WAV-файлов для достижения этой цели, однако цель состоит в том, чтобы повторить то, что Windows делает с обучением профиля речи.
Я вставил ниже полный код с обработкой ошибок. , заголовочный файл с оболочкой C и код C# опущен в надежде, что кто-нибудь сможет помочь.
Возникли следующие проблемы:
- Файл .wav создается, но не воспроизводится. Файл можно открыть
в программе для редактирования аудио, и он воспроизводится с очень высокой
скоростью. - Чтение второго обучающего текста (например, StarListening), wav файл
перезаписан. - AppendTranscript приводит к ошибке FAILED (ошибка p!=0).
CComPtr cpRecognizer2;
CComPtr cpRecognizer;
CComPtr cpProfileToken;
CComPtr cpRecoContext;
CComPtr cpRecoGrammar;
CComPtr cpAudio;
CComPtr cpObjectToken;
CComPtr cpTranscript;
CComPtr cpRecoProfile;
CComPtr cpRecoResult;
CComPtr cpStream;
CComPtr cpStreamFormat;
WCHAR *pwszRecoProfileName;
WCHAR* pwszRecoResultText;
CSpDynamicString TrText;
ULONG CSIDL_LOCAL_APPDATA = 28;
ULONG CSIDL_FLAG_CREATE = 32768;
LoopFunciton для распознавания и создания WAV-файлов на основе профиля речи по умолчанию.
void LoopFunction()
{
HRESULT hr = S_OK;
while (running.load()) {
// Wait for recognition events
CSpEvent spEvent;
while (spEvent.GetFrom(cpRecoContext) == S_OK)
{
if (spEvent.eEventId == SPEI_RECOGNITION)
{
// Get the reco result
cpRecoResult = spEvent.RecoResult();
// release recognition result pointer in event object
spEvent.Clear();
StopListening();
// if recognition received, and result stored then store the audio
if (cpRecoResult)
{
//GetStorageFileName retrieves the object token file name from the registry.
LPWSTR lpFileName;
hr = cpObjectToken->GetStorageFileName(CLSID_NULL, L"TrainingAudio.wav", L"TrainingAudio\\ESTE_%d.wav", CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, &lpFileName);
// Get da recognized text
hr = cpRecoResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, FALSE, &pwszRecoResultText, NULL);
// get stream pointer to recognized audio
hr = cpRecoResult->GetAudio(0, 0, &cpStreamFormat);
// Create a stream object
hr = cpStream.CoCreateInstance(CLSID_SpStream);
// create file on hard-disk for storing recognized audio, and Bind the stream to the file
hr = cpStream->BindToFile(lpFileName, SPFM_CREATE_ALWAYS, NULL, NULL, SPFEI_ALL_EVENTS);
ULONG cbWritten = 0;
//Continuously transfer data between the two streams until no more data is found (i.e. end of stream)
while (TRUE)
{
STATSTG stats;
hr = cpStreamFormat->Stat(&stats, NULL);
BYTE bBuffer[1000];
ULONG cbRead;
hr = cpStreamFormat->Read(bBuffer, 1000, &cbRead);
if (SUCCEEDED(hr) && cbRead > 0)
{
hr = cpStream->Write(bBuffer, cbRead, &cbWritten);
}
if (cbRead < 1000)
{
break;
}
}
// Use QueryInterface on the stream object to obtain the Transrcript
hr = cpStream->QueryInterface(&cpTranscript);
// Clear Transcript and Append trainingText to stream
hr = cpTranscript->AppendTranscript(NULL);
hr = cpTranscript->AppendTranscript(TrText);
// explicitly close the file-based stream to flush file data and allow app to immediately use the file
hr = cpStream->Close();
::CoTaskMemFree(lpFileName);
}
}
}
}
}
CreateRecoEngine настраивает все из профиля речи по умолчанию. Обратный вызов для приложения C# выполнен.
void CreateRecoEngine(void(*callback)(bool))
{
HRESULT hr = CoInitialize(NULL);
// Create InprocRecognizer
hr = cpRecognizer2.CoCreateInstance(CLSID_SpInprocRecognizer);
// Query ISpRecognizer for use with ISpRecognizer2 to access SetTrainingState
hr = cpRecognizer2.QueryInterface(&cpRecognizer);
// Get the default audio input token.
hr = SpGetDefaultTokenFromCategoryId(SPCAT_AUDIOIN, &cpObjectToken);
// Set the audio input to our token.
hr = cpRecognizer->SetInput(cpObjectToken, TRUE);
// Create the default audio input object.
hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &cpAudio);
// Set the audio input to our object.
hr = cpRecognizer->SetInput(cpAudio, TRUE);
// Get the default recognition profile
hr = SpGetDefaultTokenFromCategoryId(SPCAT_RECOPROFILES, &cpRecoProfile);
// Assign the default recognition profile to recognizer
hr = cpRecognizer->SetRecoProfile(cpRecoProfile);
// Set Reco state to Active always
hr = cpRecognizer->SetRecoState(SPRST_ACTIVE_ALWAYS);
// Create recognition context
hr = cpRecognizer->CreateRecoContext(&cpRecoContext);
// Set up the grammar
hr = cpRecoContext->CreateGrammar(1, &cpRecoGrammar);
// Load a simple dictation grammar
hr = cpRecoGrammar->LoadDictation(NULL, SPLO_STATIC);
// Turn on training
hr = cpRecognizer2->SetTrainingState(TRUE, TRUE);
// Activate retained audio settings with default format (that is, SR engine recognition format).
hr = cpRecoContext->SetAudioOptions(SPAO_RETAIN_AUDIO, &GUID_NULL, NULL);
// Run recognition loop on seperate thread
running = true;
loopThread = std::thread(LoopFunction);
callback(true);
}
StartListening получает обучающий текст из приложения C# и передает его в глобальную переменную для использования в LoopFunciton.
void StartListening(const char* trainingText)
{
HRESULT hr = S_OK;
// Set global variable to traingText
TrText = trainingText;
// Activate the grammar
hr = cpRecoGrammar->SetDictationState(SPRS_ACTIVE);
}
void StopListening()
{
HRESULT hr = S_OK;
// Stop recognition loop
running = false;
// Turn off dictation
hr = cpRecoGrammar->SetDictationState(SPRS_INACTIVE);
// Turn off Training
hr = cpRecognizer2->SetTrainingState(FALSE, TRUE);
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... g-sapi-5-4