diff options
author | henrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-06 21:07:52 +0000 |
---|---|---|
committer | henrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-06 21:07:52 +0000 |
commit | 627e7bc136622483f09138505a2c695194ffea0a (patch) | |
tree | a25b314f6c04ce3f2bb1a694f5e74e750150fa1c | |
parent | 16969f048808af3157a8d75bee3cd285a5527f7e (diff) | |
download | chromium_src-627e7bc136622483f09138505a2c695194ffea0a.zip chromium_src-627e7bc136622483f09138505a2c695194ffea0a.tar.gz chromium_src-627e7bc136622483f09138505a2c695194ffea0a.tar.bz2 |
Adds input volume control support for Windows platforms.
This CL also adds a device_id input parameter to media::GetAudioInputHardwareSampleRate() and media::GetAudioInputHardwareChannelCount(). We need this new flexibility to be able to perform unit tests where the volume is modified for all supported devices. Without it, we will not be able to open the audio-input stream using the correct sample rate and channels count for all devices since all we can get information about is the default device.
BUG=115013
TEST=media_unittests
Review URL: http://codereview.chromium.org/9585010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125222 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/renderer_host/render_message_filter.cc | 9 | ||||
-rw-r--r-- | content/renderer/media/webrtc_audio_device_unittest.cc | 16 | ||||
-rw-r--r-- | content/test/webrtc_audio_device_test.cc | 6 | ||||
-rw-r--r-- | content/test/webrtc_audio_device_test.h | 6 | ||||
-rw-r--r-- | media/audio/audio_input_volume_unittest.cc | 42 | ||||
-rw-r--r-- | media/audio/audio_low_latency_input_output_unittest.cc | 3 | ||||
-rw-r--r-- | media/audio/audio_util.cc | 22 | ||||
-rw-r--r-- | media/audio/audio_util.h | 11 | ||||
-rw-r--r-- | media/audio/win/audio_low_latency_input_win.cc | 150 | ||||
-rw-r--r-- | media/audio/win/audio_low_latency_input_win.h | 39 | ||||
-rw-r--r-- | media/audio/win/audio_low_latency_input_win_unittest.cc | 38 | ||||
-rw-r--r-- | media/audio/win/wavein_input_win.cc | 6 | ||||
-rw-r--r-- | media/audio/win/wavein_input_win.h | 1 |
13 files changed, 230 insertions, 119 deletions
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index 806d2b1..646f94d 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -42,6 +42,7 @@ #include "content/public/common/url_constants.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_platform_file.h" +#include "media/audio/audio_manager_base.h" #include "media/audio/audio_util.h" #include "media/base/media_log_event.h" #include "net/base/cookie_monster.h" @@ -666,7 +667,9 @@ void RenderMessageFilter::OnGetHardwareBufferSize(uint32* buffer_size) { } void RenderMessageFilter::OnGetHardwareInputSampleRate(double* sample_rate) { - *sample_rate = media::GetAudioInputHardwareSampleRate(); + // TODO(henrika): add support for all available input devices. + *sample_rate = media::GetAudioInputHardwareSampleRate( + AudioManagerBase::kDefaultDeviceId); } void RenderMessageFilter::OnGetHardwareSampleRate(double* sample_rate) { @@ -674,7 +677,9 @@ void RenderMessageFilter::OnGetHardwareSampleRate(double* sample_rate) { } void RenderMessageFilter::OnGetHardwareInputChannelCount(uint32* channels) { - *channels = static_cast<uint32>(media::GetAudioInputHardwareChannelCount()); + // TODO(henrika): add support for all available input devices. + *channels = static_cast<uint32>(media::GetAudioInputHardwareChannelCount( + AudioManagerBase::kDefaultDeviceId)); } void RenderMessageFilter::OnDownloadUrl(const IPC::Message& message, diff --git a/content/renderer/media/webrtc_audio_device_unittest.cc b/content/renderer/media/webrtc_audio_device_unittest.cc index 577302b..33507f0 100644 --- a/content/renderer/media/webrtc_audio_device_unittest.cc +++ b/content/renderer/media/webrtc_audio_device_unittest.cc @@ -35,11 +35,13 @@ class AudioUtil : public AudioUtilInterface { virtual double GetAudioHardwareSampleRate() OVERRIDE { return media::GetAudioHardwareSampleRate(); } - virtual double GetAudioInputHardwareSampleRate() OVERRIDE { - return media::GetAudioInputHardwareSampleRate(); + virtual double GetAudioInputHardwareSampleRate( + const std::string& device_id) OVERRIDE { + return media::GetAudioInputHardwareSampleRate(device_id); } - virtual uint32 GetAudioInputHardwareChannelCount() OVERRIDE { - return media::GetAudioInputHardwareChannelCount(); + virtual uint32 GetAudioInputHardwareChannelCount( + const std::string& device_id) OVERRIDE { + return media::GetAudioInputHardwareChannelCount(device_id); } private: DISALLOW_COPY_AND_ASSIGN(AudioUtil); @@ -57,10 +59,12 @@ class AudioUtilNoHardware : public AudioUtilInterface { virtual double GetAudioHardwareSampleRate() OVERRIDE { return output_rate_; } - virtual double GetAudioInputHardwareSampleRate() OVERRIDE { + virtual double GetAudioInputHardwareSampleRate( + const std::string& device_id) OVERRIDE { return input_rate_; } - virtual uint32 GetAudioInputHardwareChannelCount() OVERRIDE { + virtual uint32 GetAudioInputHardwareChannelCount( + const std::string& device_id) OVERRIDE { return input_channels_; } diff --git a/content/test/webrtc_audio_device_test.cc b/content/test/webrtc_audio_device_test.cc index 3d077cc..08f5717 100644 --- a/content/test/webrtc_audio_device_test.cc +++ b/content/test/webrtc_audio_device_test.cc @@ -219,13 +219,15 @@ void WebRTCAudioDeviceTest::OnGetHardwareSampleRate(double* sample_rate) { void WebRTCAudioDeviceTest::OnGetHardwareInputSampleRate(double* sample_rate) { EXPECT_TRUE(audio_util_callback_); *sample_rate = audio_util_callback_ ? - audio_util_callback_->GetAudioInputHardwareSampleRate() : 0.0; + audio_util_callback_->GetAudioInputHardwareSampleRate( + AudioManagerBase::kDefaultDeviceId) : 0.0; } void WebRTCAudioDeviceTest::OnGetHardwareInputChannelCount(uint32* channels) { EXPECT_TRUE(audio_util_callback_); *channels = audio_util_callback_ ? - audio_util_callback_->GetAudioInputHardwareChannelCount() : 0; + audio_util_callback_->GetAudioInputHardwareChannelCount( + AudioManagerBase::kDefaultDeviceId) : 0; } // IPC::Channel::Listener implementation. diff --git a/content/test/webrtc_audio_device_test.h b/content/test/webrtc_audio_device_test.h index 2b07ad9..8b93e83 100644 --- a/content/test/webrtc_audio_device_test.h +++ b/content/test/webrtc_audio_device_test.h @@ -109,8 +109,10 @@ class AudioUtilInterface { public: virtual ~AudioUtilInterface() {} virtual double GetAudioHardwareSampleRate() = 0; - virtual double GetAudioInputHardwareSampleRate() = 0; - virtual uint32 GetAudioInputHardwareChannelCount() = 0; + virtual double GetAudioInputHardwareSampleRate( + const std::string& device_id) = 0; + virtual uint32 GetAudioInputHardwareChannelCount( + const std::string& device_id) = 0; }; // Implemented and defined in the cc file. diff --git a/media/audio/audio_input_volume_unittest.cc b/media/audio/audio_input_volume_unittest.cc index 5a006f0..15cb7fb 100644 --- a/media/audio/audio_input_volume_unittest.cc +++ b/media/audio/audio_input_volume_unittest.cc @@ -22,6 +22,13 @@ class AudioInputVolumeTest : public ::testing::Test { } bool CanRunAudioTests() { +#if defined(OS_WIN) + // TODO(henrika): add support for volume control on Windows XP as well. + // For now, we might as well signal false already here to avoid running + // these tests on Windows XP. + if (!media::IsWASAPISupported()) + return false; +#endif if (!audio_manager_.get()) return false; @@ -38,28 +45,22 @@ class AudioInputVolumeTest : public ::testing::Test { AudioInputStream* CreateAndOpenStream(const std::string& device_id) { AudioParameters::Format format = AudioParameters::AUDIO_PCM_LOW_LATENCY; - // TODO(xians): Implement a generic HardwareChannelCount API to query - // the number of channel for all the devices. ChannelLayout channel_layout = - (media::GetAudioInputHardwareChannelCount() == 1) ? + (media::GetAudioInputHardwareChannelCount(device_id) == 1) ? CHANNEL_LAYOUT_MONO : CHANNEL_LAYOUT_STEREO; int bits_per_sample = 16; int sample_rate = - static_cast<int>(media::GetAudioInputHardwareSampleRate()); + static_cast<int>(media::GetAudioInputHardwareSampleRate(device_id)); int samples_per_packet = 0; #if defined(OS_MACOSX) samples_per_packet = (sample_rate / 100); #elif defined(OS_LINUX) || defined(OS_OPENBSD) samples_per_packet = (sample_rate / 100); #elif defined(OS_WIN) - if (media::IsWASAPISupported()) { - if (sample_rate == 44100) - samples_per_packet = 448; - else - samples_per_packet = (sample_rate / 100); - } else { - samples_per_packet = 3 * (sample_rate / 100); - } + if (sample_rate == 44100) + samples_per_packet = 448; + else + samples_per_packet = (sample_rate / 100); #else #error Unsupported platform #endif @@ -93,10 +94,12 @@ TEST_F(AudioInputVolumeTest, InputVolumeTest) { if (!CanRunAudioTests()) return; + // Retrieve a list of all available input devices. AudioDeviceNames device_names; audio_manager_->GetAudioInputDeviceNames(&device_names); DCHECK(!device_names.empty()); + // Scan all available input devices and repeat the same test for all of them. for (AudioDeviceNames::const_iterator it = device_names.begin(); it != device_names.end(); ++it) { @@ -106,9 +109,9 @@ TEST_F(AudioInputVolumeTest, InputVolumeTest) { continue; } - if ( !HasDeviceVolumeControl(ais)) { + if (!HasDeviceVolumeControl(ais)) { DLOG(WARNING) << "Device: " << it->unique_id - << ", does not have volume control"; + << ", does not have volume control."; ais->Close(); continue; } @@ -116,30 +119,33 @@ TEST_F(AudioInputVolumeTest, InputVolumeTest) { double max_volume = ais->GetMaxVolume(); EXPECT_GT(max_volume, 0.0); - // Notes that |original_volume| can be higher than |max_volume| on Linux. + // Store the current input-device volume level. double original_volume = ais->GetVolume(); EXPECT_GE(original_volume, 0.0); #if defined(OS_WIN) || defined(OS_MACOSX) + // Note that |original_volume| can be higher than |max_volume| on Linux. EXPECT_LE(original_volume, max_volume); #endif - // Tries to set the volume to |max_volume|. + // Set the volume to the maxiumum level.. ais->SetVolume(max_volume); double current_volume = ais->GetVolume(); EXPECT_EQ(max_volume, current_volume); - // Tries to set the volume to zero. + // Set the volume to the mininum level (=0). double new_volume = 0.0; ais->SetVolume(new_volume); current_volume = ais->GetVolume(); EXPECT_EQ(new_volume, current_volume); - // Tries to set the volume to the middle. + // Set the volume to the mid level (50% of max). + // Verify that the absolute error is small enough. new_volume = max_volume / 2; ais->SetVolume(new_volume); current_volume = ais->GetVolume(); EXPECT_LT(current_volume, max_volume); EXPECT_GT(current_volume, 0); + EXPECT_NEAR(current_volume, new_volume, 0.25 * max_volume); // Restores the volume to the original value. ais->SetVolume(original_volume); diff --git a/media/audio/audio_low_latency_input_output_unittest.cc b/media/audio/audio_low_latency_input_output_unittest.cc index 9ad8a98..57b3d4d 100644 --- a/media/audio/audio_low_latency_input_output_unittest.cc +++ b/media/audio/audio_low_latency_input_output_unittest.cc @@ -274,7 +274,8 @@ class AudioInputStreamTraits { typedef AudioInputStream StreamType; static int HardwareSampleRate() { - return static_cast<int>(media::GetAudioInputHardwareSampleRate()); + return static_cast<int>(media::GetAudioInputHardwareSampleRate( + AudioManagerBase::kDefaultDeviceId)); } static StreamType* CreateStream(AudioManager* audio_manager, diff --git a/media/audio/audio_util.cc b/media/audio/audio_util.cc index 0ce05fa..b387ce7 100644 --- a/media/audio/audio_util.cc +++ b/media/audio/audio_util.cc @@ -17,6 +17,7 @@ #include "base/time.h" #if defined(OS_WIN) #include "base/win/windows_version.h" +#include "media/audio/audio_manager_base.h" #endif #include "media/audio/audio_parameters.h" #include "media/audio/audio_util.h" @@ -262,24 +263,17 @@ double GetAudioHardwareSampleRate() { #endif } -double GetAudioInputHardwareSampleRate() { +double GetAudioInputHardwareSampleRate(const std::string& device_id) { + // TODO(henrika): add support for device selection on all platforms. + // Only exists on Windows today. #if defined(OS_MACOSX) - // Hardware sample-rate on the Mac can be configured, so we must query. return AUAudioInputStream::HardwareSampleRate(); #elif defined(OS_WIN) if (!IsWASAPISupported()) { - // Fall back to Windows Wave implementation on Windows XP or lower - // and use 48kHz as default input sample rate. return 48000.0; } - - // Hardware sample-rate on Windows can be configured, so we must query. - // TODO(henrika): improve possibility to specify audio endpoint. - // Use the default device (same as for Wave) for now to be compatible. - return WASAPIAudioInputStream::HardwareSampleRate(eConsole); + return WASAPIAudioInputStream::HardwareSampleRate(device_id); #else - // Hardware for Linux is nearly always 48KHz. - // TODO(henrika): return correct value in rare non-48KHz cases. return 48000.0; #endif } @@ -315,7 +309,9 @@ size_t GetAudioHardwareBufferSize() { #endif } -uint32 GetAudioInputHardwareChannelCount() { +uint32 GetAudioInputHardwareChannelCount(const std::string& device_id) { + // TODO(henrika): add support for device selection on all platforms. + // Only exists on Windows today. enum channel_layout { MONO = 1, STEREO = 2 }; #if defined(OS_MACOSX) return MONO; @@ -325,7 +321,7 @@ uint32 GetAudioInputHardwareChannelCount() { // use stereo by default. return STEREO; } - return WASAPIAudioInputStream::HardwareChannelCount(eConsole); + return WASAPIAudioInputStream::HardwareChannelCount(device_id); #else return STEREO; #endif diff --git a/media/audio/audio_util.h b/media/audio/audio_util.h index e2f57fa..c57f2c2 100644 --- a/media/audio/audio_util.h +++ b/media/audio/audio_util.h @@ -5,6 +5,7 @@ #ifndef MEDIA_AUDIO_AUDIO_UTIL_H_ #define MEDIA_AUDIO_AUDIO_UTIL_H_ +#include <string> #include <vector> #include "base/basictypes.h" @@ -84,16 +85,18 @@ MEDIA_EXPORT void InterleaveFloatToInt16(const std::vector<float*>& source, // Returns the default audio output hardware sample-rate. MEDIA_EXPORT double GetAudioHardwareSampleRate(); -// Returns the default audio input hardware sample-rate. -MEDIA_EXPORT double GetAudioInputHardwareSampleRate(); +// Returns the audio input hardware sample-rate for the specified device. +MEDIA_EXPORT double GetAudioInputHardwareSampleRate( + const std::string& device_id); // Returns the optimal low-latency buffer size for the audio hardware. // This is the smallest buffer size the system can comfortably render // at without glitches. The buffer size is in sample-frames. MEDIA_EXPORT size_t GetAudioHardwareBufferSize(); -// Returns the default number of channels for the audio input hardware. -MEDIA_EXPORT uint32 GetAudioInputHardwareChannelCount(); +// Returns the number of channels for the specified audio input device. +MEDIA_EXPORT uint32 GetAudioInputHardwareChannelCount( + const std::string& device_id); // Functions that handle data buffer passed between processes in the shared // memory. Called on both IPC sides. diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc index 708d889..839ace8 100644 --- a/media/audio/win/audio_low_latency_input_win.cc +++ b/media/audio/win/audio_low_latency_input_win.cc @@ -75,6 +75,7 @@ WASAPIAudioInputStream::WASAPIAudioInputStream( WASAPIAudioInputStream::~WASAPIAudioInputStream() {} bool WASAPIAudioInputStream::Open() { + DCHECK(CalledOnValidThread()); // Verify that we are not already opened. if (opened_) return false; @@ -83,23 +84,21 @@ bool WASAPIAudioInputStream::Open() { // device with the specified unique identifier or role which was // set at construction. HRESULT hr = SetCaptureDevice(); - if (FAILED(hr)) { + if (FAILED(hr)) return false; - } // Obtain an IAudioClient interface which enables us to create and initialize // an audio stream between an audio application and the audio engine. hr = ActivateCaptureDevice(); - if (FAILED(hr)) { + if (FAILED(hr)) return false; - } // Retrieve the stream format which the audio engine uses for its internal - // processing/mixing of shared-mode streams. + // processing/mixing of shared-mode streams. This function call is for + // diagnostic purposes only and only in debug mode. +#ifndef NDEBUG hr = GetAudioEngineStreamFormat(); - if (FAILED(hr)) { - return false; - } +#endif // Verify that the selected audio endpoint supports the specified format // set during construction. @@ -110,16 +109,13 @@ bool WASAPIAudioInputStream::Open() { // Initialize the audio stream between the client and the device using // shared mode and a lowest possible glitch-free latency. hr = InitializeAudioEngine(); - if (FAILED(hr)) { - return false; - } - - opened_ = true; - return true; + opened_ = SUCCEEDED(hr); + return opened_; } void WASAPIAudioInputStream::Start(AudioInputCallback* callback) { + DCHECK(CalledOnValidThread()); DCHECK(callback); DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully"; if (!opened_) @@ -144,6 +140,7 @@ void WASAPIAudioInputStream::Start(AudioInputCallback* callback) { } void WASAPIAudioInputStream::Stop() { + DCHECK(CalledOnValidThread()); if (!started_) return; @@ -183,23 +180,51 @@ void WASAPIAudioInputStream::Close() { } double WASAPIAudioInputStream::GetMaxVolume() { - // TODO(xians): Add volume support. - return 0.0; + // Verify that Open() has been called succesfully, to ensure that an audio + // session exists and that an ISimpleAudioVolume interface has been created. + DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully"; + if (!opened_) + return 0.0; + + // The effective volume value is always in the range 0.0 to 1.0, hence + // we can return a fixed value (=1.0) here. + return 1.0; } void WASAPIAudioInputStream::SetVolume(double volume) { - // TODO(xians): Add volume support. + DCHECK(CalledOnValidThread()); + DCHECK(volume <= 1.0 && volume >= 0.0); + + DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully"; + if (!opened_) + return; + + // Set a new master volume level. Valid volume levels are in the range + // 0.0 to 1.0. Ignore volume-change events. + HRESULT hr = simple_audio_volume_->SetMasterVolume(static_cast<float>(volume), + NULL); + DLOG_IF(WARNING, FAILED(hr)) << "Failed to set new input master volume."; } double WASAPIAudioInputStream::GetVolume() { - // TODO(xians): Add volume support. - return 0.0; + DCHECK(CalledOnValidThread()); + DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully"; + if (!opened_) + return 0.0; + + // Retrieve the current volume level. The value is in the range 0.0 to 1.0. + float level = 0.0f; + HRESULT hr = simple_audio_volume_->GetMasterVolume(&level); + DLOG_IF(WARNING, FAILED(hr)) << "Failed to get input master volume."; + + return static_cast<double>(level); } // static -double WASAPIAudioInputStream::HardwareSampleRate(ERole device_role) { +double WASAPIAudioInputStream::HardwareSampleRate( + const std::string& device_id) { base::win::ScopedCoMem<WAVEFORMATEX> audio_engine_mix_format; - HRESULT hr = GetMixFormat(device_role, &audio_engine_mix_format); + HRESULT hr = GetMixFormat(device_id, &audio_engine_mix_format); if (FAILED(hr)) return 0.0; @@ -207,9 +232,10 @@ double WASAPIAudioInputStream::HardwareSampleRate(ERole device_role) { } // static -uint32 WASAPIAudioInputStream::HardwareChannelCount(ERole device_role) { +uint32 WASAPIAudioInputStream::HardwareChannelCount( + const std::string& device_id) { base::win::ScopedCoMem<WAVEFORMATEX> audio_engine_mix_format; - HRESULT hr = GetMixFormat(device_role, &audio_engine_mix_format); + HRESULT hr = GetMixFormat(device_id, &audio_engine_mix_format); if (FAILED(hr)) return 0; @@ -217,7 +243,7 @@ uint32 WASAPIAudioInputStream::HardwareChannelCount(ERole device_role) { } // static -HRESULT WASAPIAudioInputStream::GetMixFormat(ERole device_role, +HRESULT WASAPIAudioInputStream::GetMixFormat(const std::string& device_id, WAVEFORMATEX** device_format) { // It is assumed that this static method is called from a COM thread, i.e., // CoInitializeEx() is not called here to avoid STA/MTA conflicts. @@ -231,9 +257,16 @@ HRESULT WASAPIAudioInputStream::GetMixFormat(ERole device_role, return hr; ScopedComPtr<IMMDevice> endpoint_device; - hr = enumerator->GetDefaultAudioEndpoint(eCapture, - device_role, - endpoint_device.Receive()); + if (device_id == AudioManagerBase::kDefaultDeviceId) { + // Retrieve the default capture audio endpoint. + hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole, + endpoint_device.Receive()); + } else { + // Retrieve a capture endpoint device that is specified by an endpoint + // device-identification string. + hr = enumerator->GetDevice(UTF8ToUTF16(device_id).c_str(), + endpoint_device.Receive()); + } if (FAILED(hr)) { // This will happen if there's no audio capture device found or available // (e.g. some audio cards that have inputs will still report them as @@ -417,7 +450,7 @@ HRESULT WASAPIAudioInputStream::SetCaptureDevice() { if (SUCCEEDED(hr)) { // Retrieve the IMMDevice by using the specified role or the specified // unique endpoint device-identification string. - // TODO(henrika): possibly add suport for the eCommunications as well. + // TODO(henrika): possibly add support for the eCommunications as well. if (device_id_ == AudioManagerBase::kDefaultDeviceId) { // Retrieve the default capture audio endpoint for the specified role. // Note that, in Windows Vista, the MMDevice API supports device roles @@ -461,17 +494,55 @@ HRESULT WASAPIAudioInputStream::ActivateCaptureDevice() { } HRESULT WASAPIAudioInputStream::GetAudioEngineStreamFormat() { - // Retrieve the stream format that the audio engine uses for its internal - // processing/mixing of shared-mode streams. - return audio_client_->GetMixFormat(&audio_engine_mix_format_); + HRESULT hr = S_OK; +#ifndef NDEBUG + // The GetMixFormat() method retrieves the stream format that the + // audio engine uses for its internal processing of shared-mode streams. + // The method always uses a WAVEFORMATEXTENSIBLE structure, instead + // of a stand-alone WAVEFORMATEX structure, to specify the format. + // An WAVEFORMATEXTENSIBLE structure can specify both the mapping of + // channels to speakers and the number of bits of precision in each sample. + base::win::ScopedCoMem<WAVEFORMATEXTENSIBLE> format_ex; + hr = audio_client_->GetMixFormat( + reinterpret_cast<WAVEFORMATEX**>(&format_ex)); + + // See http://msdn.microsoft.com/en-us/windows/hardware/gg463006#EFH + // for details on the WAVE file format. + WAVEFORMATEX format = format_ex->Format; + DVLOG(2) << "WAVEFORMATEX:"; + DVLOG(2) << " wFormatTags : 0x" << std::hex << format.wFormatTag; + DVLOG(2) << " nChannels : " << format.nChannels; + DVLOG(2) << " nSamplesPerSec : " << format.nSamplesPerSec; + DVLOG(2) << " nAvgBytesPerSec: " << format.nAvgBytesPerSec; + DVLOG(2) << " nBlockAlign : " << format.nBlockAlign; + DVLOG(2) << " wBitsPerSample : " << format.wBitsPerSample; + DVLOG(2) << " cbSize : " << format.cbSize; + + DVLOG(2) << "WAVEFORMATEXTENSIBLE:"; + DVLOG(2) << " wValidBitsPerSample: " << + format_ex->Samples.wValidBitsPerSample; + DVLOG(2) << " dwChannelMask : 0x" << std::hex << + format_ex->dwChannelMask; + if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) + DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_PCM"; + else if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) + DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT"; + else if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_WAVEFORMATEX) + DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_WAVEFORMATEX"; +#endif + return hr; } bool WASAPIAudioInputStream::DesiredFormatIsSupported() { - // In shared mode, the audio engine always supports the mix format, - // which is stored in the |audio_engine_mix_format_| member. In addition, - // the audio engine *might* support similar formats that have the same - // sample rate and number of channels as the mix format but differ in - // the representation of audio sample values. + // An application that uses WASAPI to manage shared-mode streams can rely + // on the audio engine to perform only limited format conversions. The audio + // engine can convert between a standard PCM sample size used by the + // application and the floating-point samples that the engine uses for its + // internal processing. However, the format for an application stream + // typically must have the same number of channels and the same sample + // rate as the stream format used by the device. + // Many audio devices support both PCM and non-PCM stream formats. However, + // the audio engine can mix only PCM streams. base::win::ScopedCoMem<WAVEFORMATEX> closest_match; HRESULT hr = audio_client_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &format_, @@ -545,5 +616,12 @@ HRESULT WASAPIAudioInputStream::InitializeAudioEngine() { // enables us to read input data from the capture endpoint buffer. hr = audio_client_->GetService(__uuidof(IAudioCaptureClient), audio_capture_client_.ReceiveVoid()); + if (FAILED(hr)) + return hr; + + // Obtain a reference to the ISimpleAudioVolume interface which enables + // us to control the master volume level of an audio session. + hr = audio_client_->GetService(__uuidof(ISimpleAudioVolume), + simple_audio_volume_.ReceiveVoid()); return hr; } diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h index 8f11feb..d34c025 100644 --- a/media/audio/win/audio_low_latency_input_win.h +++ b/media/audio/win/audio_low_latency_input_win.h @@ -49,6 +49,13 @@ // audio buffer is event driven. // - The Multimedia Class Scheduler service (MMCSS) is utilized to boost // the priority of the capture thread. +// - Audio applications that use the MMDevice API and WASAPI typically use +// the ISimpleAudioVolume interface to manage stream volume levels on a +// per-session basis. It is also possible to use of the IAudioEndpointVolume +// interface to control the master volume level of an audio endpoint device. +// This implementation is using the ISimpleAudioVolume interface. +// MSDN states that "In rare cases, a specialized audio application might +// require the use of the IAudioEndpointVolume". // #ifndef MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_INPUT_WIN_H_ #define MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_INPUT_WIN_H_ @@ -56,7 +63,10 @@ #include <Audioclient.h> #include <MMDeviceAPI.h> +#include <string> + #include "base/compiler_specific.h" +#include "base/threading/non_thread_safe.h" #include "base/threading/platform_thread.h" #include "base/threading/simple_thread.h" #include "base/win/scoped_co_mem.h" @@ -72,7 +82,8 @@ class AudioManagerWin; // AudioInputStream implementation using Windows Core Audio APIs. class MEDIA_EXPORT WASAPIAudioInputStream : public AudioInputStream, - public base::DelegateSimpleThread::Delegate { + public base::DelegateSimpleThread::Delegate, + NON_EXPORTED_BASE(public base::NonThreadSafe) { public: // The ctor takes all the usual parameters, plus |manager| which is the // the audio manager who is creating this object. @@ -93,12 +104,12 @@ class MEDIA_EXPORT WASAPIAudioInputStream virtual double GetVolume() OVERRIDE; // Retrieves the sample rate used by the audio engine for its internal - // processing/mixing of shared-mode streams. - static double HardwareSampleRate(ERole device_role); + // processing/mixing of shared-mode streams given a specifed device. + static double HardwareSampleRate(const std::string& device_id); // Retrieves the number of audio channels used by the audio engine for its - // internal processing/mixing of shared-mode streams. - static uint32 HardwareChannelCount(ERole device_role); + // internal processing/mixing of shared-mode streams given a specifed device. + static uint32 HardwareChannelCount(const std::string& device_id); bool started() const { return started_; } @@ -118,7 +129,8 @@ class MEDIA_EXPORT WASAPIAudioInputStream // Retrieves the stream format that the audio engine uses for its internal // processing/mixing of shared-mode streams. - static HRESULT GetMixFormat(ERole device_role, WAVEFORMATEX** device_format); + static HRESULT GetMixFormat(const std::string& device_id, + WAVEFORMATEX** device_format); // Initializes the COM library for use by the calling thread and set the // thread's concurrency model to multi-threaded. @@ -134,11 +146,6 @@ class MEDIA_EXPORT WASAPIAudioInputStream // Contains the desired audio format which is set up at construction. WAVEFORMATEX format_; - // Copy of the audio format which we know the audio engine supports. - // It is recommended to ensure that the sample rate in |format_| is identical - // to the sample rate in |audio_engine_mix_format_|. - base::win::ScopedCoMem<WAVEFORMATEX> audio_engine_mix_format_; - bool opened_; bool started_; @@ -172,9 +179,13 @@ class MEDIA_EXPORT WASAPIAudioInputStream // Pointer to the object that will receive the recorded audio samples. AudioInputCallback* sink_; + // Windows Multimedia Device (MMDevice) API interfaces. + // An IMMDevice interface which represents an audio endpoint device. base::win::ScopedComPtr<IMMDevice> endpoint_device_; + // Windows Audio Session API (WASAP) interfaces. + // An IAudioClient interface which enables a client to create and initialize // an audio stream between an audio application and the audio engine. base::win::ScopedComPtr<IAudioClient> audio_client_; @@ -183,6 +194,12 @@ class MEDIA_EXPORT WASAPIAudioInputStream // from a capture endpoint buffer. base::win::ScopedComPtr<IAudioCaptureClient> audio_capture_client_; + // The ISimpleAudioVolume interface enables a client to control the + // master volume level of an audio session. + // The volume-level is a value in the range 0.0 to 1.0. + // This interface does only work with shared-mode streams. + base::win::ScopedComPtr<ISimpleAudioVolume> simple_audio_volume_; + // The audio engine will signal this event each time a buffer has been // recorded. base::win::ScopedHandle audio_samples_ready_event_; diff --git a/media/audio/win/audio_low_latency_input_win_unittest.cc b/media/audio/win/audio_low_latency_input_win_unittest.cc index a77620a..c4f6f9d 100644 --- a/media/audio/win/audio_low_latency_input_win_unittest.cc +++ b/media/audio/win/audio_low_latency_input_win_unittest.cc @@ -116,7 +116,8 @@ class AudioInputStreamWrapper { bits_per_sample_(16) { // Use native/mixing sample rate and 10ms frame size as default. sample_rate_ = static_cast<int>( - WASAPIAudioInputStream::HardwareSampleRate(eConsole)); + WASAPIAudioInputStream::HardwareSampleRate( + AudioManagerBase::kDefaultDeviceId)); samples_per_packet_ = sample_rate_ / 100; } @@ -170,10 +171,7 @@ static AudioInputStream* CreateDefaultAudioInputStream( } // Verify that we can retrieve the current hardware/mixing sample rate -// for all supported device roles. The ERole enumeration defines constants -// that indicate the role that the system/user has assigned to an audio -// endpoint device. -// TODO(henrika): modify this test when we support full device enumeration. +// for all available input devices. TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); if (!CanRunAudioTests(audio_manager.get())) @@ -181,21 +179,19 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); - // Default device intended for games, system notification sounds, - // and voice commands. - int fs = static_cast<int>( - WASAPIAudioInputStream::HardwareSampleRate(eConsole)); - EXPECT_GE(fs, 0); - - // Default communication device intended for e.g. VoIP communication. - fs = static_cast<int>( - WASAPIAudioInputStream::HardwareSampleRate(eCommunications)); - EXPECT_GE(fs, 0); - - // Multimedia device for music, movies and live music recording. - fs = static_cast<int>( - WASAPIAudioInputStream::HardwareSampleRate(eMultimedia)); - EXPECT_GE(fs, 0); + // Retrieve a list of all available input devices. + media::AudioDeviceNames device_names; + audio_manager->GetAudioInputDeviceNames(&device_names); + + // Scan all available input devices and repeat the same test for all of them. + for (media::AudioDeviceNames::const_iterator it = device_names.begin(); + it != device_names.end(); ++it) { + // Retrieve the hardware sample rate given a specified audio input device. + // TODO(tommi): ensure that we don't have to cast here. + int fs = static_cast<int>(WASAPIAudioInputStream::HardwareSampleRate( + it->unique_id)); + EXPECT_GE(fs, 0); + } } // Test Create(), Close() calling sequence. @@ -374,7 +370,7 @@ TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { LOG(INFO) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; WriteToFileAudioSink file_sink(file_name); - LOG(INFO) << ">> Speak into the microphone while recording."; + LOG(INFO) << ">> Speak into the default microphone while recording."; ais->Start(&file_sink); base::PlatformThread::Sleep(TestTimeouts::action_timeout_ms()); ais->Stop(); diff --git a/media/audio/win/wavein_input_win.cc b/media/audio/win/wavein_input_win.cc index 61d4b9f..10f0df0 100644 --- a/media/audio/win/wavein_input_win.cc +++ b/media/audio/win/wavein_input_win.cc @@ -188,16 +188,16 @@ void PCMWaveInAudioInputStream::Close() { } double PCMWaveInAudioInputStream::GetMaxVolume() { - // TODO(xians): Add volume support. + // TODO(henrika): Add volume support using the Audio Mixer API. return 0.0; } void PCMWaveInAudioInputStream::SetVolume(double volume) { - // TODO(xians): Add volume support. + // TODO(henrika): Add volume support using the Audio Mixer API. } double PCMWaveInAudioInputStream::GetVolume() { - // TODO(xians): Add volume support. + // TODO(henrika): Add volume support using the Audio Mixer API. return 0.0; } diff --git a/media/audio/win/wavein_input_win.h b/media/audio/win/wavein_input_win.h index 2ea6e08..66465a8 100644 --- a/media/audio/win/wavein_input_win.h +++ b/media/audio/win/wavein_input_win.h @@ -32,6 +32,7 @@ class PCMWaveInAudioInputStream : public AudioInputStream { virtual void Start(AudioInputCallback* callback) OVERRIDE; virtual void Stop() OVERRIDE; virtual void Close() OVERRIDE; + // TODO(henrika): Add volume support using the Audio Mixer API. virtual double GetMaxVolume() OVERRIDE; virtual void SetVolume(double volume) OVERRIDE; virtual double GetVolume() OVERRIDE; |