Commit 1d62ab1f authored by Henrik Grunell's avatar Henrik Grunell Committed by Commit Bot

Add Windows input audio voice capture DMO for voice processing.

Controlled by existing origin trial.

Bug: 845187
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I43e4e0d9f303086b50e9dd57ae547bfdcd79f204
Reviewed-on: https://chromium-review.googlesource.com/1065918
Commit-Queue: Henrik Grunell <grunell@chromium.org>
Reviewed-by: default avatarTommi <tommi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#560677}
parent 979ca1f9
......@@ -222,9 +222,13 @@ source_set("audio") {
]
libs += [
"dmoguids.lib",
"dxguid.lib",
"msdmo.lib",
"setupapi.lib",
"strmiids.lib",
"winmm.lib",
"wmcodecdspuuid.lib",
]
}
......
......@@ -33,6 +33,8 @@ class AudioOutputDispatcher;
// AudioManagerBase provides AudioManager functions common for all platforms.
class MEDIA_EXPORT AudioManagerBase : public AudioManager {
public:
enum class VoiceProcessingMode { kDisabled = 0, kEnabled = 1 };
~AudioManagerBase() override;
AudioOutputStream* MakeAudioOutputStream(
......
......@@ -20,7 +20,6 @@
#include "base/sys_info.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "media/audio/mac/audio_manager_mac.h"
#include "media/audio/mac/scoped_audio_unit.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_timestamp_helper.h"
......@@ -165,7 +164,7 @@ AUAudioInputStream::AUAudioInputStream(
const AudioParameters& input_params,
AudioDeviceID audio_device_id,
const AudioManager::LogCallback& log_callback,
VoiceProcessingMode voice_processing_mode)
AudioManagerBase::VoiceProcessingMode voice_processing_mode)
: manager_(manager),
input_params_(input_params),
number_of_frames_provided_(0),
......@@ -184,7 +183,7 @@ AUAudioInputStream::AUAudioInputStream(
audio_unit_render_has_worked_(false),
noise_reduction_suppressed_(false),
use_voice_processing_(voice_processing_mode ==
VoiceProcessingMode::ENABLED),
AudioManagerBase::VoiceProcessingMode::kEnabled),
last_sample_time_(0.0),
last_number_of_frames_(0),
total_lost_frames_(0),
......
......@@ -50,26 +50,23 @@
#include "base/timer/timer.h"
#include "media/audio/agc_audio_stream.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
#include "media/audio/mac/audio_manager_mac.h"
#include "media/base/audio_block_fifo.h"
#include "media/base/audio_parameters.h"
namespace media {
class AudioManagerMac;
class MEDIA_EXPORT AUAudioInputStream
: public AgcAudioStream<AudioInputStream> {
public:
enum class VoiceProcessingMode { DISABLED = 0, ENABLED = 1 };
// The ctor takes all the usual parameters, plus |manager| which is the
// the audio manager who is creating this object.
AUAudioInputStream(AudioManagerMac* manager,
const AudioParameters& input_params,
AudioDeviceID audio_device_id,
const AudioManager::LogCallback& log_callback,
VoiceProcessingMode voice_processing_mode);
AUAudioInputStream(
AudioManagerMac* manager,
const AudioParameters& input_params,
AudioDeviceID audio_device_id,
const AudioManager::LogCallback& log_callback,
AudioManagerBase::VoiceProcessingMode voice_processing_mode);
// The dtor is typically called by the AudioManager only and it is usually
// triggered by calling AudioInputStream::Close().
~AUAudioInputStream() override;
......
......@@ -795,11 +795,10 @@ AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
return nullptr;
}
using VoiceProcessingMode = AUAudioInputStream::VoiceProcessingMode;
VoiceProcessingMode voice_processing_mode =
(params.effects() & AudioParameters::ECHO_CANCELLER)
? VoiceProcessingMode::ENABLED
: VoiceProcessingMode::DISABLED;
? VoiceProcessingMode::kEnabled
: VoiceProcessingMode::kDisabled;
auto* stream = new AUAudioInputStream(this, params, audio_device_id,
log_callback, voice_processing_mode);
......
......@@ -58,6 +58,7 @@
#include <Audioclient.h>
#include <MMDeviceAPI.h>
#include <dmo.h>
#include <endpointvolume.h>
#include <stddef.h>
#include <stdint.h>
......@@ -75,7 +76,7 @@
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_handle.h"
#include "media/audio/agc_audio_stream.h"
#include "media/audio/audio_manager.h"
#include "media/audio/win/audio_manager_win.h"
#include "media/base/audio_converter.h"
#include "media/base/audio_parameters.h"
#include "media/base/media_export.h"
......@@ -84,7 +85,6 @@ namespace media {
class AudioBlockFifo;
class AudioBus;
class AudioManagerWin;
// AudioInputStream implementation using Windows Core Audio APIs.
class MEDIA_EXPORT WASAPIAudioInputStream
......@@ -94,10 +94,12 @@ class MEDIA_EXPORT WASAPIAudioInputStream
public:
// The ctor takes all the usual parameters, plus |manager| which is the
// the audio manager who is creating this object.
WASAPIAudioInputStream(AudioManagerWin* manager,
const AudioParameters& params,
const std::string& device_id,
const AudioManager::LogCallback& log_callback);
WASAPIAudioInputStream(
AudioManagerWin* manager,
const AudioParameters& params,
const std::string& device_id,
const AudioManager::LogCallback& log_callback,
AudioManagerBase::VoiceProcessingMode voice_processing_mode);
// The dtor is typically called by the AudioManager only and it is usually
// triggered by calling AudioInputStream::Close().
......@@ -117,16 +119,27 @@ class MEDIA_EXPORT WASAPIAudioInputStream
bool started() const { return started_; }
private:
// DelegateSimpleThread::Delegate implementation.
// DelegateSimpleThread::Delegate implementation. Calls either
// RunWithAudioCaptureClient() or RunWithDmo().
void Run() override;
// Pulls capture data from the endpoint device and pushes it to the sink.
// Waits for an event that the audio capture client has data ready.
bool RunWithAudioCaptureClient();
// Polls the DMO (voice processing component) for data every 5 ms.
bool RunWithDmo();
// Pulls capture data from the audio capture client and pushes it to the sink.
void PullCaptureDataAndPushToSink();
// Pulls capture data from the DMO and pushes it to the sink.
void PullDmoCaptureDataAndPushToSink();
// Issues the OnError() callback to the |sink_|.
void HandleError(HRESULT err);
// The Open() method is divided into these sub methods.
// The Open() method is divided into these sub methods when not using the
// voice processing DMO.
HRESULT SetCaptureDevice();
HRESULT GetAudioEngineStreamFormat();
// Returns whether the desired format is supported or not and writes the
......@@ -134,14 +147,22 @@ class MEDIA_EXPORT WASAPIAudioInputStream
// function returns false with |*hr| == S_FALSE, the OS supports a closest
// match but we don't support conversion to it.
bool DesiredFormatIsSupported(HRESULT* hr);
void SetupConverterAndStoreFormatInfo();
HRESULT InitializeAudioEngine();
void ReportOpenResult(HRESULT hr) const;
// Reports stats for format related audio client initilization
// (IAudioClient::Initialize) errors, that is if |hr| is an error related to
// the format.
void MaybeReportFormatRelatedInitError(HRESULT hr) const;
// The Open() method is divided into these sub methods when using the voice
// processing DMO. In addition, SetupConverterAndStoreFormatInfo() above is
// also called.
bool InitializeDmo();
bool SetDmoProperties();
bool SetDmoFormat();
bool CreateDummyRenderClientsForDmo();
// AudioConverter::InputCallback implementation.
double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
......@@ -189,30 +210,35 @@ class MEDIA_EXPORT WASAPIAudioInputStream
std::unique_ptr<base::DelegateSimpleThread> capture_thread_;
// Contains the desired output audio format which is set up at construction,
// that is the audio format this class should output data to the sink in.
// that is the audio format this class should output data to the sink in, that
// is the format after the converter.
WAVEFORMATEX output_format_;
// Contains the audio format we get data from the audio engine in. Set to
// |output_format_| at construction and might be changed to a close match
// if the audio engine doesn't support the originally set format.
// if the audio engine doesn't support the originally set format, or to the
// format the voice capture DMO outputs if it's used. Note that this is also
// the format after the fifo, i.e. the input format to the converter if any.
WAVEFORMATEX input_format_;
bool opened_ = false;
bool started_ = false;
StreamOpenResult open_result_ = OPEN_RESULT_OK;
// Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM)
size_t frame_size_ = 0;
// Size in bytes of each audio frame before the converter (4 bytes for 16-bit
// stereo PCM). Note that this is the same before and after the fifo.
size_t frame_size_bytes_ = 0;
// Size in audio frames of each audio packet where an audio packet
// is defined as the block of data which the user received in each
// OnData() callback.
// Size in audio frames of each audio packet (buffer) after the fifo but
// before the converter.
size_t packet_size_frames_ = 0;
// Size in bytes of each audio packet.
// Size in bytes of each audio packet (buffer) after the fifo but before the
// converter.
size_t packet_size_bytes_ = 0;
// Length of the audio endpoint buffer.
// Length of the audio endpoint buffer, or the buffer size used for the DMO.
// That is, the buffer size before the fifo.
uint32_t endpoint_buffer_size_frames_ = 0;
// Contains the unique name of the selected endpoint device.
......@@ -295,6 +321,22 @@ class MEDIA_EXPORT WASAPIAudioInputStream
UINT64 total_lost_frames_ = 0;
UINT64 largest_glitch_frames_ = 0;
// Indicates if the voice processing DMO should be used.
bool use_voice_processing_ = false;
// The voice processing DMO and its data buffer.
Microsoft::WRL::ComPtr<IMediaObject> voice_capture_dmo_;
Microsoft::WRL::ComPtr<IMediaBuffer> media_buffer_;
// Dummy rendering when using the DMO. The DMO requires audio rendering to the
// device it's set up to use, otherwise it won't produce any capture audio
// data. Normally, when the DMO is used there's a render stream, but it's not
// guaranteed so we need to support the lack of it. We do this by always
// opening a render client and rendering silence to it when the DMO is
// running.
Microsoft::WRL::ComPtr<IAudioClient> audio_client_for_render_;
Microsoft::WRL::ComPtr<IAudioRenderClient> audio_render_client_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(WASAPIAudioInputStream);
......
......@@ -170,11 +170,16 @@ static bool HasCoreAudioAndInputDevices(AudioManager* audio_man) {
// also allows the user to modify the default settings.
class AudioInputStreamWrapper {
public:
explicit AudioInputStreamWrapper(AudioManager* audio_manager)
explicit AudioInputStreamWrapper(AudioManager* audio_manager,
bool use_voice_processing)
: audio_man_(audio_manager) {
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
AudioDeviceDescription::kDefaultDeviceId, false, &default_params_)));
EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
if (use_voice_processing) {
default_params_.set_effects(default_params_.effects() |
AudioParameters::ECHO_CANCELLER);
}
frames_per_buffer_ = default_params_.frames_per_buffer();
}
......@@ -222,8 +227,9 @@ class AudioInputStreamWrapper {
// Convenience method which creates a default AudioInputStream object.
static AudioInputStream* CreateDefaultAudioInputStream(
AudioManager* audio_manager) {
AudioInputStreamWrapper aisw(audio_manager);
AudioManager* audio_manager,
bool use_voice_processing) {
AudioInputStreamWrapper aisw(audio_manager, use_voice_processing);
AudioInputStream* ais = aisw.Create();
return ais;
}
......@@ -258,7 +264,9 @@ class ScopedAudioInputStream {
DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
};
class WinAudioInputTest : public ::testing::Test {
// The test class. The boolean parameter specifies if voice processing should be
// used.
class WinAudioInputTest : public ::testing::TestWithParam<bool> {
public:
WinAudioInputTest() {
audio_manager_ =
......@@ -293,27 +301,27 @@ TEST_F(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
}
// Test Create(), Close() calling sequence.
TEST_F(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
TEST_P(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
ScopedAudioInputStream ais(
CreateDefaultAudioInputStream(audio_manager_.get()));
CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
ais.Close();
}
// Test Open(), Close() calling sequence.
TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
TEST_P(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
ScopedAudioInputStream ais(
CreateDefaultAudioInputStream(audio_manager_.get()));
CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
EXPECT_TRUE(ais->Open());
ais.Close();
}
// Test Open(), Start(), Close() calling sequence.
TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
TEST_P(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
ScopedAudioInputStream ais(
CreateDefaultAudioInputStream(audio_manager_.get()));
CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
EXPECT_TRUE(ais->Open());
MockAudioInputCallback sink;
ais->Start(&sink);
......@@ -321,10 +329,10 @@ TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
}
// Test Open(), Start(), Stop(), Close() calling sequence.
TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
TEST_P(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
ScopedAudioInputStream ais(
CreateDefaultAudioInputStream(audio_manager_.get()));
CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
EXPECT_TRUE(ais->Open());
MockAudioInputCallback sink;
ais->Start(&sink);
......@@ -333,10 +341,10 @@ TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
}
// Test some additional calling sequences.
TEST_F(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
TEST_P(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
ScopedAudioInputStream ais(
CreateDefaultAudioInputStream(audio_manager_.get()));
CreateDefaultAudioInputStream(audio_manager_.get(), GetParam()));
// Open(), Open() should fail the second time.
EXPECT_TRUE(ais->Open());
......@@ -358,7 +366,7 @@ TEST_F(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
ais.Close();
}
TEST_F(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
TEST_P(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
int count = 0;
......@@ -367,7 +375,7 @@ TEST_F(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
// Create default WASAPI input stream which records in stereo using
// the shared mixing rate. The default buffer size is 10ms.
AudioInputStreamWrapper aisw(audio_manager_.get());
AudioInputStreamWrapper aisw(audio_manager_.get(), GetParam());
ScopedAudioInputStream ais(aisw.Create());
EXPECT_TRUE(ais->Open());
......@@ -441,7 +449,7 @@ TEST_F(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
}
// Test that we can capture a stream in loopback.
TEST_F(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
TEST_P(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
AudioDeviceInfoAccessorForTests device_info_accessor(audio_manager_.get());
ABORT_AUDIO_TEST_IF_NOT(device_info_accessor.HasAudioOutputDevices() &&
CoreAudioUtil::IsSupported());
......@@ -475,7 +483,7 @@ TEST_F(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
// To include disabled tests in test execution, just invoke the test program
// with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
// environment variable to a value greater than 0.
TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
TEST_P(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
// Name of the output PCM file containing captured data. The output file
......@@ -483,7 +491,7 @@ TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
// Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
const char* file_name = "out_10sec.pcm";
AudioInputStreamWrapper aisw(audio_manager_.get());
AudioInputStreamWrapper aisw(audio_manager_.get(), GetParam());
ScopedAudioInputStream ais(aisw.Create());
ASSERT_TRUE(ais->Open());
......@@ -497,7 +505,7 @@ TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
ais.Close();
}
TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamResampleToFile) {
TEST_P(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamResampleToFile) {
ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
// This is basically the same test as WASAPIAudioInputStreamRecordToFile
......@@ -532,6 +540,8 @@ TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamResampleToFile) {
// Otherwise (e.g. 44.1kHz, 22.05kHz etc) we convert to 48kHz.
const int hw_sample_rate = params.sample_rate();
params.Reset(params.format(), test.layout, test.rate, test.frames);
if (GetParam())
params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER);
std::string file_name(base::StringPrintf(
"resampled_10sec_%i_to_%i_%s.pcm", hw_sample_rate, params.sample_rate(),
......@@ -555,4 +565,8 @@ TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamResampleToFile) {
}
}
INSTANTIATE_TEST_CASE_P(/* Intentially left empty */,
WinAudioInputTest,
::testing::Bool());
} // namespace media
......@@ -23,6 +23,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/win/windows_version.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/audio_features.h"
#include "media/audio/audio_io.h"
#include "media/audio/win/audio_device_listener_win.h"
#include "media/audio/win/audio_low_latency_input_win.h"
......@@ -178,6 +179,9 @@ AudioParameters AudioManagerWin::GetInputStreamParameters(
if (user_buffer_size)
parameters.set_frames_per_buffer(user_buffer_size);
parameters.set_effects(parameters.effects() |
AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
return parameters;
}
......@@ -248,7 +252,14 @@ AudioInputStream* AudioManagerWin::MakeLowLatencyInputStream(
const LogCallback& log_callback) {
// Used for both AUDIO_PCM_LOW_LATENCY and AUDIO_PCM_LINEAR.
DVLOG(1) << "MakeLowLatencyInputStream: " << device_id;
return new WASAPIAudioInputStream(this, params, device_id, log_callback);
VoiceProcessingMode voice_processing_mode =
params.effects() & AudioParameters::ECHO_CANCELLER
? VoiceProcessingMode::kEnabled
: VoiceProcessingMode::kDisabled;
return new WASAPIAudioInputStream(this, params, device_id, log_callback,
voice_processing_mode);
}
std::string AudioManagerWin::GetDefaultInputDeviceID() {
......
......@@ -976,4 +976,71 @@ bool CoreAudioUtil::GetDxDiagDetails(std::string* driver_name,
return true;
}
HRESULT CoreAudioUtil::GetDeviceCollectionIndex(const std::string& device_id,
EDataFlow data_flow,
WORD* index) {
ComPtr<IMMDeviceEnumerator> enumerator = CreateDeviceEnumerator();
if (!enumerator.Get()) {
DLOG(ERROR) << "Failed to create device enumerator.";
return E_FAIL;
}
ComPtr<IMMDeviceCollection> device_collection;
HRESULT hr = enumerator->EnumAudioEndpoints(data_flow, DEVICE_STATE_ACTIVE,
&device_collection);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get device collection.";
return hr;
}
UINT number_of_devices = 0;
hr = device_collection->GetCount(&number_of_devices);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get device collection count.";
return hr;
}
ComPtr<IMMDevice> device;
for (WORD i = 0; i < number_of_devices; ++i) {
hr = device_collection->Item(i, &device);
if (FAILED(hr)) {
DLOG(WARNING) << "Failed to get device.";
continue;
}
ScopedCoMem<WCHAR> current_device_id;
hr = device->GetId(&current_device_id);
if (FAILED(hr)) {
DLOG(WARNING) << "Failed to get device id.";
continue;
}
if (base::UTF16ToUTF8(current_device_id.get()) == device_id) {
*index = i;
return S_OK;
}
}
DVLOG(1) << "No matching device found.";
return S_FALSE;
}
HRESULT CoreAudioUtil::SetBoolProperty(IPropertyStore* property_store,
REFPROPERTYKEY key,
VARIANT_BOOL value) {
base::win::ScopedPropVariant pv;
PROPVARIANT* pv_ptr = pv.Receive();
pv_ptr->vt = VT_BOOL;
pv_ptr->boolVal = value;
return property_store->SetValue(key, pv.get());
}
HRESULT CoreAudioUtil::SetVtI4Property(IPropertyStore* property_store,
REFPROPERTYKEY key,
LONG value) {
base::win::ScopedPropVariant pv;
PROPVARIANT* pv_ptr = pv.Receive();
pv_ptr->vt = VT_I4;
pv_ptr->lVal = value;
return property_store->SetValue(key, pv.get());
}
} // namespace media
......@@ -212,6 +212,23 @@ class MEDIA_EXPORT CoreAudioUtil {
static bool GetDxDiagDetails(std::string* driver_name,
std::string* driver_version);
// Gets the device collection index for the device specified by |device_id|.
// If the device is found in the device collection, the index is written to
// |*index| and S_OK is returned. If the device is not found, S_FALSE is
// returned and |*index| is left unchanged. In case of an error, the error
// result is returned and |*index| is left unchanged.
static HRESULT GetDeviceCollectionIndex(const std::string& device_id,
EDataFlow data_flow,
WORD* index);
// Sets the property identified by |key| to |value| in |*property_store|.
static HRESULT SetBoolProperty(IPropertyStore* property_store,
REFPROPERTYKEY key,
VARIANT_BOOL value);
static HRESULT SetVtI4Property(IPropertyStore* property_store,
REFPROPERTYKEY key,
LONG value);
private:
CoreAudioUtil() {}
~CoreAudioUtil() {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment