Как программно определить аудиовыход в Windows 10?

Я разрабатываю приложение C++, которое реализует Microsoft Speech API (SAPI). Я разработал множество функций, связанных с преобразованием текста в речь. Среди них функция, позволяющая вывести список аудиовыходов, и функция, позволяющая определить аудиовыход.

Я начал разрабатывать эту программу на Windows 7, но теперь перешел на Windows 10. Однако функция, определяющая вывод звука, больше не работает. Я ничего не редактировал в своем коде, и в Windows 7 он работал отлично.

Вот код, в котором перечислены доступные аудиовыходы

int getAudioOut( int auOut ) //get audio outputs function
{
    if( SUCCEEDED( hr ) )
    {
       //Enumerate Audio Outputs
       hr = SpEnumTokens( SPCAT_AUDIOOUT, NULL, NULL, &cpEnum );
       cpEnum->GetCount( &vCount );
       cpEnum->Item( saveAudio, &cpAudioOutToken );
       SpGetDescription( cpAudioOutToken, &dynStr );
       printf( "Defined audio output is: %ls\n\n", dynStr );
       dynStr.Clear();

       //Loop through the audio output list and enumerate them all
       for( audioOut = 0; audioOut <= vCount - 1; audioOut++ )
       {
          cpAudioOutToken.Release();
          cpEnum->Item( audioOut, &cpAudioOutToken );
          SpGetDescription( cpAudioOutToken, &dynStr );
          printf( "Defined Audio Output %i - %ls\n", audioOut, dynStr );
          dynStr.Clear();
       }
       printf( "\n" );
       audioOut = saveAudio;

       cpEnum.Release();
       cpAudioOutToken.Release();
    }
    else
    {
       printf( "Could not enumerate available audio outputs\n" );
    }

    return true;
}

Вот код, который позволяет определить аудиовыход

int setAudioOut( int auOut ) //define audio output function
{
   if( SUCCEEDED( hr ) )
   {
      hr = SpEnumTokens( SPCAT_AUDIOOUT, NULL, NULL, &cpEnum );
      cpEnum->GetCount( &vCount );
      size_t nOut = auOut;

      if( nOut >= vCount )
      {
         cout << "Not so many audio outputs available! Try again\n" << endl;
      }
      else
      {
         cout << "Success" << endl;
      }

       ULONG audioOut = static_cast<ULONG>( nOut ); //convert nOut to ULONG audioOut

       cpEnum->Item( audioOut, &cpAudioOutToken );
       SpGetDescription( cpAudioOutToken, &dynStr );
       printf( "You chose %ls\n\n", dynStr );
       cpVoice->SetOutput( cpAudioOutToken, TRUE ); //Initialization of the Audio Output
       dynStr.Clear();

       cpEnum.Release();
       cpAudioOutToken.Release();

       saveAudio = audioOut; //define saveAudio to audioOut value
    }
    else
    {
       printf( "Could not set audio output\n" );
    }

    return true;
}

Когда я запускаю свою программу и вызываю функцию getAudioOut, я получаю следующий листинг:

список функций getAudioOut

В первой строке показан аудиовыход по умолчанию, а в двух нижних — доступные выходы. В Windows 7, когда я устанавливаю второй аудиовыход (Lautsprecher/Kopfhörer) по умолчанию, то из первого (Digitalaudio) звук не выходит, что имеет смысл. Однако в Windows 10 я воспроизвел ту же процедуру, но она не работает. Аудиовыход всегда определяется в соответствии с меню аудио.

Аудиоменю

Мой вопрос: кто-нибудь сталкивался с этой проблемой? Есть ли альтернатива программному определению аудиовыхода?


person georges619    schedule 09.05.2019    source источник
comment
Извините, код, который вы пишете, не имеет для меня особого смысла. Например, что это за строка для auOut = auOut;? Запускать cpAudioOutToken.Release() внутри и вне цикла тоже не лучшая идея. Цикл do/while во второй функции тоже безумен. Может быть, вы сначала попытаетесь очистить свой код, тогда проблема станет намного яснее. Или он может даже исчезнуть в конце концов.   -  person Nikolay Shmyrev    schedule 09.05.2019
comment
Я изменил код, как вы упомянули, но это ничего не меняет:/   -  person georges619    schedule 10.05.2019
comment
Я узнал ошибку! Это произошло из-за другой функции. Выкладываю решение ниже.   -  person georges619    schedule 10.05.2019


Ответы (1)


Я отредактировал код, как предложил @NikolayShmyrev, но это ничего не изменило. Однако я продолжил копаться в проблеме и обнаружил, что проблема возникла из-за другой функции. Действительно, когда я перешел с Windows 7 на Windows 10, я столкнулся с другими проблемами с функцией синтеза речи и функцией преобразования речи в файл WAV. Когда я запустил программу и вызвал функцию Text-To-Speech, все отлично заработало. Когда я вызвал функцию Speech2Wav, она тоже сработала. Однако, когда я вспомнил функцию Text-To-Speech, переменная HRESULT hr = S_OK; изменила свое значение и звук не воспроизводился. Значение hr установлено на -2147200968, что соответствует ошибке 0x80045038: SPERR_STREAM_CLOSED (источник/список кодов ошибок)

Чтобы решить эту проблему, мне пришлось определить такой аудиовыход cpVoice->SetOutput( cpAudioOutToken, TRUE ); в функции Text-To-Speech.

Это возвращает нас к проблеме, о которой я говорил выше. Когда я устанавливаю аудиовыход в функции setAudioOut, я освобождаю его значение в конце cpAudioOutToken.Release(); Однако я повторно использую ту же переменную в функции Text-To-Speech. Его значение было установлено равным нулю, потому что я отключил его, когда определял аудиовыход. Вот почему аудиовыход всегда был установлен по умолчанию. Чтобы решить эту проблему, я присвоил значение cpAudioOutToken другой переменной с именем cpSpeechOutToken.

Вот код функции setAudioOut

int setAudioOut( int auOut ) //define audio output function
{
   if( SUCCEEDED( hr ) )
   {
      hr = SpEnumTokens( SPCAT_AUDIOOUT, NULL, NULL, &cpEnum );
      cpEnum->GetCount( &vCount );
      size_t nOut = auOut;

      if( nOut >= vCount )
      {
         cout << "Not so many audio outputs available! Try again\n" << endl;
         return 0;
      }
      else
      {
         cout << "Success" << endl;
      }

      ULONG audioOut = static_cast<ULONG>( nOut ); //convert nOut to ULONG audioOut

      cpEnum->Item( audioOut, &cpAudioOutToken );
      SpGetDescription( cpAudioOutToken, &dynStr );
      printf( "You chose %ls\n\n", dynStr );
      cpVoice->SetOutput( cpAudioOutToken, TRUE ); //Initialization of the Audio Output
      dynStr.Clear();

      cpEnum.Release();
      cpSpeechOutToken = cpAudioOutToken;
      cpAudioOutToken.Release();
      saveAudio = audioOut; //define saveAudio to audioOut value
   }
   else
   {
      printf( "Could not set audio output\n" );
   }
   return true;
}

Вот код из функции Text-To-Speech

int ttsSpeak( const char* text ) //Text to Speech speaking function
{
   if( SUCCEEDED( hr ) )
   {
      string xmlSentence( text );
      hr = SpEnumTokens( SPCAT_VOICES_WIN10, NULL, NULL, &cpEnum );
      //Replace SPCAT_VOICES_WIN10 with SPCAT_VOICES if you want to use it on Windows 7

      cpEnum->Item( saveVoice, &cpVoiceToken ); //get saveVoice token defined at line 175
      cpVoice->SetVoice( cpVoiceToken ); //Initialization of the voice

      //string strText( text );

      int wchars_num = MultiByteToWideChar( CP_ACP, 0, xmlSentence.c_str(), -1, NULL, 0 );
      wchar_t* wstr = new wchar_t[ wchars_num ];
      MultiByteToWideChar( CP_ACP, 0, xmlSentence.c_str(), -1, wstr, wchars_num );

      printf( "Text To Speech processing\n" );
      cpVoice->SetOutput( cpSpeechOutToken, TRUE );
      hr = cpVoice->Speak( wstr, SVSFIsXML, NULL );

      saveText = xmlSentence.c_str();

      cpEnum.Release();
      cpVoiceToken.Release();
      delete new wchar_t[ wchars_num ];
  }
  else
  {
     printf( "Could not speak entered text\n" );
  }
  return true;
}
person georges619    schedule 10.05.2019