Выбор аудиоустройства ввода с помощью пакета SDK для распознавания речи
В этой статье описывается, как получить идентификаторы звуковых устройств, подключенных к системе. Эти идентификаторы затем можно использовать в пакете SDK для службы "Речь", чтобы выбрать входные звуковые данные. Настройка звукового устройства выполняется с помощью объекта AudioConfig
:
audioConfig = AudioConfig.FromMicrophoneInput("<device id>");
audioConfig = AudioConfig.FromMicrophoneInput("<device id>");
audio_config = AudioConfig(device_name="<device id>");
audioConfig = AudioConfiguration.FromMicrophoneInput("<device id>");
audioConfig = AudioConfiguration.fromMicrophoneInput("<device id>");
audioConfig = AudioConfiguration.fromMicrophoneInput("<device id>");
Примечание.
При использовании JavaScript на платформе Node.js использовать микрофон невозможно.
Идентификаторы звуковых устройств в Windows для классических приложений
Строки идентификаторов конечной точки звукового устройства можно извлечь из объекта IMMDevice
в Windows для классических приложений.
В следующем образце кода показано, как использовать его для перечисления аудиоустройств в C++:
#include <cstdio>
#include <mmdeviceapi.h>
#include <Functiondiscoverykeys_devpkey.h>
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
constexpr auto REFTIMES_PER_SEC = (10000000 * 25);
constexpr auto REFTIMES_PER_MILLISEC = 10000;
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
void ListEndpoints();
int main()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
ListEndpoints();
}
//-----------------------------------------------------------
// This function enumerates all active (plugged in) audio
// rendering endpoint devices. It prints the friendly name
// and endpoint ID string of each endpoint device.
//-----------------------------------------------------------
void ListEndpoints()
{
HRESULT hr = S_OK;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pCollection = NULL;
IMMDevice *pEndpoint = NULL;
IPropertyStore *pProps = NULL;
LPWSTR pwszID = NULL;
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
EXIT_ON_ERROR(hr);
hr = pEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pCollection);
EXIT_ON_ERROR(hr);
UINT count;
hr = pCollection->GetCount(&count);
EXIT_ON_ERROR(hr);
if (count == 0)
{
printf("No endpoints found.\n");
}
// Each iteration prints the name of an endpoint device.
PROPVARIANT varName;
for (ULONG i = 0; i < count; i++)
{
// Get the pointer to endpoint number i.
hr = pCollection->Item(i, &pEndpoint);
EXIT_ON_ERROR(hr);
// Get the endpoint ID string.
hr = pEndpoint->GetId(&pwszID);
EXIT_ON_ERROR(hr);
hr = pEndpoint->OpenPropertyStore(
STGM_READ, &pProps);
EXIT_ON_ERROR(hr);
// Initialize the container for property value.
PropVariantInit(&varName);
// Get the endpoint's friendly-name property.
hr = pProps->GetValue(PKEY_Device_FriendlyName, &varName);
EXIT_ON_ERROR(hr);
// Print the endpoint friendly name and endpoint ID.
printf("Endpoint %d: \"%S\" (%S)\n", i, varName.pwszVal, pwszID);
CoTaskMemFree(pwszID);
pwszID = NULL;
PropVariantClear(&varName);
}
Exit:
CoTaskMemFree(pwszID);
pwszID = NULL;
PropVariantClear(&varName);
SAFE_RELEASE(pEnumerator);
SAFE_RELEASE(pCollection);
SAFE_RELEASE(pEndpoint);
SAFE_RELEASE(pProps);
}
В языке C# библиотеку NAudio можно использовать для доступа к API CoreAudio и перечисления устройств следующим образом:
using System;
using NAudio.CoreAudioApi;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var enumerator = new MMDeviceEnumerator();
foreach (var endpoint in
enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active))
{
Console.WriteLine("{0} ({1})", endpoint.FriendlyName, endpoint.ID);
}
}
}
}
Пример идентификатора устройства: {0.0.1.00000000}.{5f23ab69-6181-4f4a-81a4-45414013aac8}
.
Идентификатор аудиоустройства в UWP
На универсальной платформе Windows (UWP) звуковые устройства ввода можно получить с помощью свойства Id()
соответствующего объекта DeviceInformation
.
В приведенных ниже примерах кода показано, как это сделать в языках C++ и C#.
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Devices.Enumeration.h>
using namespace winrt::Windows::Devices::Enumeration;
void enumerateDeviceIds()
{
auto promise = DeviceInformation::FindAllAsync(DeviceClass::AudioCapture);
promise.Completed(
[](winrt::Windows::Foundation::IAsyncOperation<DeviceInformationCollection> const& sender,
winrt::Windows::Foundation::AsyncStatus /* asyncStatus */) {
auto info = sender.GetResults();
auto num_devices = info.Size();
for (const auto &device : info)
{
std::wstringstream ss{};
ss << "looking at device (of " << num_devices << "): " << device.Id().c_str() << "\n";
OutputDebugString(ss.str().c_str());
}
});
}
using Windows.Devices.Enumeration;
using System.Linq;
namespace helloworld {
private async void EnumerateDevices()
{
var devices = await DeviceInformation.FindAllAsync(DeviceClass.AudioCapture);
foreach (var device in devices)
{
Console.WriteLine($"{device.Name}, {device.Id}\n");
}
}
}
Пример идентификатора устройства: \\\\?\\SWD#MMDEVAPI#{0.0.1.00000000}.{5f23ab69-6181-4f4a-81a4-45414013aac8}#{2eef81be-33fa-4800-9670-1cd474972c3f}
.
Идентификаторы аудиоустройств в Linux
Идентификаторы устройств выбираются с помощью стандартных идентификаторов устройств ALSA.
Идентификаторы средств ввода, подключенных к системе, содержатся в выходных данных команды arecord -L
.
Кроме того, их можно получить из библиотеки ALSA для C.
Примеры идентификаторов: hw:1,0
и hw:CARD=CC,DEV=0
.
Идентификаторы аудиоустройств на macOS
Следующая функция, реализованная в Objective-C, создает список имен и идентификаторов аудиоустройств, подключенных к Mac.
Строка deviceUID
используется для идентификации устройства в пакете SDK для распознавания речи для macOS.
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
CFArrayRef CreateInputDeviceArray()
{
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
OSStatus status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
if (kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %i\n", status);
return NULL;
}
UInt32 deviceCount = (uint32)(dataSize / sizeof(AudioDeviceID));
AudioDeviceID *audioDevices = (AudioDeviceID *)(malloc(dataSize));
if (NULL == audioDevices) {
fputs("Unable to allocate memory", stderr);
return NULL;
}
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
if (kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %i\n", status);
free(audioDevices);
audioDevices = NULL;
return NULL;
}
CFMutableArrayRef inputDeviceArray = CFArrayCreateMutable(kCFAllocatorDefault, deviceCount, &kCFTypeArrayCallBacks);
if (NULL == inputDeviceArray) {
fputs("CFArrayCreateMutable failed", stderr);
free(audioDevices);
audioDevices = NULL;
return NULL;
}
// Iterate through all the devices and determine which are input-capable
propertyAddress.mScope = kAudioDevicePropertyScopeInput;
for (UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
if (kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID) failed: %i\n", status);
continue;
}
// Query device name
CFStringRef deviceName = NULL;
dataSize = sizeof(deviceName);
propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceName);
if (kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %i\n", status);
continue;
}
// Determine if the device is an input device (it is an input device if it has input channels)
dataSize = 0;
propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
status = AudioObjectGetPropertyDataSize(audioDevices[i], &propertyAddress, 0, NULL, &dataSize);
if (kAudioHardwareNoError != status) {
fprintf(stderr, "AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %i\n", status);
continue;
}
AudioBufferList *bufferList = (AudioBufferList *)(malloc(dataSize));
if (NULL == bufferList) {
fputs("Unable to allocate memory", stderr);
break;
}
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, bufferList);
if (kAudioHardwareNoError != status || 0 == bufferList->mNumberBuffers) {
if (kAudioHardwareNoError != status)
fprintf(stderr, "AudioObjectGetPropertyData (kAudioDevicePropertyStreamConfiguration) failed: %i\n", status);
free(bufferList);
bufferList = NULL;
continue;
}
free(bufferList);
bufferList = NULL;
// Add a dictionary for this device to the array of input devices
CFStringRef keys [] = { CFSTR("deviceUID"), CFSTR("deviceName")};
CFStringRef values [] = { deviceUID, deviceName};
CFDictionaryRef deviceDictionary = CFDictionaryCreate(kCFAllocatorDefault,
(const void **)(keys),
(const void **)(values),
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(inputDeviceArray, deviceDictionary);
CFRelease(deviceDictionary);
deviceDictionary = NULL;
}
free(audioDevices);
audioDevices = NULL;
// Return a non-mutable copy of the array
CFArrayRef immutableInputDeviceArray = CFArrayCreateCopy(kCFAllocatorDefault, inputDeviceArray);
CFRelease(inputDeviceArray);
inputDeviceArray = NULL;
return immutableInputDeviceArray;
}
К примеру, идентификатор UID для встроенного микрофона — BuiltInMicrophoneDevice
.
Идентификатор аудиоустройства в iOS
Выбор звукового устройства с помощью пакета SDK для службы "Речь" не поддерживается в системе iOS. Приложения, использующие пакет SDK, могут влиять на маршрутизацию звуковых данных через платформу AVAudioSession
.
Например, инструкция
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord
withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:NULL];
позволяет использовать гарнитуру Bluetooth для приложения с поддержкой речевых функций.
Идентификаторы звуковых устройств в JavaScript
В JavaScript метод MediaDevices.enumerateDevices() можно использовать для перечисления устройств мультимедиа и поиска идентификаторов устройств для их передачи fromMicrophone(...)
.