Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/mac/audio_low_latency_input_mac.h" | 5 #include "media/audio/mac/audio_low_latency_input_mac.h" |
| 6 | 6 |
| 7 #include <CoreServices/CoreServices.h> | 7 #include <CoreServices/CoreServices.h> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/mac/mac_logging.h" | 11 #include "base/mac/mac_logging.h" |
| 12 #include "media/audio/audio_util.h" | 12 #include "media/audio/audio_util.h" |
| 13 #include "media/audio/mac/audio_manager_mac.h" | 13 #include "media/audio/mac/audio_manager_mac.h" |
| 14 | 14 |
| 15 static const int kMinIntervalBetweenVolumeUpdatesMs = 1000; | |
| 16 | |
| 15 static std::ostream& operator<<(std::ostream& os, | 17 static std::ostream& operator<<(std::ostream& os, |
| 16 const AudioStreamBasicDescription& format) { | 18 const AudioStreamBasicDescription& format) { |
| 17 os << "sample rate : " << format.mSampleRate << std::endl | 19 os << "sample rate : " << format.mSampleRate << std::endl |
| 18 << "format ID : " << format.mFormatID << std::endl | 20 << "format ID : " << format.mFormatID << std::endl |
| 19 << "format flags : " << format.mFormatFlags << std::endl | 21 << "format flags : " << format.mFormatFlags << std::endl |
| 20 << "bytes per packet : " << format.mBytesPerPacket << std::endl | 22 << "bytes per packet : " << format.mBytesPerPacket << std::endl |
| 21 << "frames per packet : " << format.mFramesPerPacket << std::endl | 23 << "frames per packet : " << format.mFramesPerPacket << std::endl |
| 22 << "bytes per frame : " << format.mBytesPerFrame << std::endl | 24 << "bytes per frame : " << format.mBytesPerFrame << std::endl |
| 23 << "channels per frame: " << format.mChannelsPerFrame << std::endl | 25 << "channels per frame: " << format.mChannelsPerFrame << std::endl |
| 24 << "bits per channel : " << format.mBitsPerChannel; | 26 << "bits per channel : " << format.mBitsPerChannel; |
| 25 return os; | 27 return os; |
| 26 } | 28 } |
| 27 | 29 |
| 28 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" | 30 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" |
| 29 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html | 31 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html |
| 30 // for more details and background regarding this implementation. | 32 // for more details and background regarding this implementation. |
| 31 | 33 |
| 32 AUAudioInputStream::AUAudioInputStream( | 34 AUAudioInputStream::AUAudioInputStream( |
| 33 AudioManagerMac* manager, const AudioParameters& params, | 35 AudioManagerMac* manager, const AudioParameters& params, |
| 34 AudioDeviceID audio_device_id) | 36 AudioDeviceID audio_device_id) |
| 35 : manager_(manager), | 37 : manager_(manager), |
| 36 sink_(NULL), | 38 sink_(NULL), |
| 37 audio_unit_(0), | 39 audio_unit_(0), |
| 38 input_device_id_(audio_device_id), | 40 input_device_id_(audio_device_id), |
| 39 started_(false), | 41 started_(false), |
| 40 hardware_latency_frames_(0), | 42 hardware_latency_frames_(0), |
| 41 number_of_channels_in_frame_(0) { | 43 number_of_channels_in_frame_(0), |
| 44 volume_(0.0), | |
| 45 agc_is_enabled_(0) { | |
| 42 DCHECK(manager_); | 46 DCHECK(manager_); |
| 43 | 47 |
| 44 // Set up the desired (output) format specified by the client. | 48 // Set up the desired (output) format specified by the client. |
| 45 format_.mSampleRate = params.sample_rate; | 49 format_.mSampleRate = params.sample_rate; |
| 46 format_.mFormatID = kAudioFormatLinearPCM; | 50 format_.mFormatID = kAudioFormatLinearPCM; |
| 47 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 51 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
| 48 kLinearPCMFormatFlagIsSignedInteger; | 52 kLinearPCMFormatFlagIsSignedInteger; |
| 49 format_.mBitsPerChannel = params.bits_per_sample; | 53 format_.mBitsPerChannel = params.bits_per_sample; |
| 50 format_.mChannelsPerFrame = params.channels; | 54 format_.mChannelsPerFrame = params.channels; |
| 51 format_.mFramesPerPacket = 1; // uncompressed audio | 55 format_.mFramesPerPacket = 1; // uncompressed audio |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 if (IsVolumeSettableOnChannel(i)) | 287 if (IsVolumeSettableOnChannel(i)) |
| 284 return 1.0; | 288 return 1.0; |
| 285 } | 289 } |
| 286 | 290 |
| 287 // Volume control is not available for the audio stream. | 291 // Volume control is not available for the audio stream. |
| 288 return 0.0; | 292 return 0.0; |
| 289 } | 293 } |
| 290 | 294 |
| 291 void AUAudioInputStream::SetVolume(double volume) { | 295 void AUAudioInputStream::SetVolume(double volume) { |
| 292 DCHECK(volume <= 1.0 && volume >= 0.0); | 296 DCHECK(volume <= 1.0 && volume >= 0.0); |
| 297 // TODO(henrika): remove! | |
| 298 DLOG(INFO) << "SetVolume(volume=" << volume << ")"; | |
|
tommi (sloooow) - chröme
2012/03/16 13:31:05
ping
henrika (OOO until Aug 14)
2012/03/21 10:16:04
Done.
| |
| 293 | 299 |
| 294 // Verify that we have a valid device. | 300 // Verify that we have a valid device. |
| 295 if (input_device_id_ == kAudioObjectUnknown) { | 301 if (input_device_id_ == kAudioObjectUnknown) { |
| 296 NOTREACHED() << "Device ID is unknown"; | 302 NOTREACHED() << "Device ID is unknown"; |
| 297 return; | 303 return; |
| 298 } | 304 } |
| 299 | 305 |
| 300 Float32 volume_float32 = static_cast<Float32>(volume); | 306 Float32 volume_float32 = static_cast<Float32>(volume); |
| 301 AudioObjectPropertyAddress property_address = { | 307 AudioObjectPropertyAddress property_address = { |
| 302 kAudioDevicePropertyVolumeScalar, | 308 kAudioDevicePropertyVolumeScalar, |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 329 NULL, | 335 NULL, |
| 330 sizeof(volume_float32), | 336 sizeof(volume_float32), |
| 331 &volume_float32); | 337 &volume_float32); |
| 332 if (result == noErr) | 338 if (result == noErr) |
| 333 ++successful_channels; | 339 ++successful_channels; |
| 334 } | 340 } |
| 335 } | 341 } |
| 336 | 342 |
| 337 DLOG_IF(WARNING, successful_channels == 0) | 343 DLOG_IF(WARNING, successful_channels == 0) |
| 338 << "Failed to set volume to " << volume_float32; | 344 << "Failed to set volume to " << volume_float32; |
| 345 | |
| 346 // We take new volume samples once every second when the AGC is enabled. | |
| 347 // To ensure that a new setting has an immediate effect, the new volume | |
| 348 // setting is cached here. It will ensure that the next OnData() callback | |
| 349 // will contain a new valid volume level. If this approach was not taken, | |
| 350 // we could report invalid volume levels to the client for a time period | |
| 351 // of up to one second. | |
| 352 if (GetAutomaticGainControl()) { | |
| 353 volume_ = GetVolume(); | |
| 354 } | |
| 339 } | 355 } |
| 340 | 356 |
| 341 double AUAudioInputStream::GetVolume() { | 357 double AUAudioInputStream::GetVolume() { |
| 342 // Verify that we have a valid device. | 358 // Verify that we have a valid device. |
| 343 if (input_device_id_ == kAudioObjectUnknown){ | 359 if (input_device_id_ == kAudioObjectUnknown){ |
| 344 NOTREACHED() << "Device ID is unknown"; | 360 NOTREACHED() << "Device ID is unknown"; |
| 345 return 0.0; | 361 return 0.0; |
| 346 } | 362 } |
| 347 | 363 |
| 348 AudioObjectPropertyAddress property_address = { | 364 AudioObjectPropertyAddress property_address = { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 389 | 405 |
| 390 // Get the average volume of the channels. | 406 // Get the average volume of the channels. |
| 391 if (successful_channels != 0) | 407 if (successful_channels != 0) |
| 392 return static_cast<double>(volume_float32 / successful_channels); | 408 return static_cast<double>(volume_float32 / successful_channels); |
| 393 } | 409 } |
| 394 | 410 |
| 395 DLOG(WARNING) << "Failed to get volume"; | 411 DLOG(WARNING) << "Failed to get volume"; |
| 396 return 0.0; | 412 return 0.0; |
| 397 } | 413 } |
| 398 | 414 |
| 415 void AUAudioInputStream::SetAutomaticGainControl(bool enabled) { | |
| 416 base::subtle::Release_Store(&agc_is_enabled_, enabled); | |
| 417 } | |
| 418 | |
| 419 bool AUAudioInputStream::GetAutomaticGainControl() { | |
| 420 return (base::subtle::Acquire_Load(&agc_is_enabled_) == 1); | |
| 421 } | |
| 422 | |
| 399 // AUHAL AudioDeviceOutput unit callback | 423 // AUHAL AudioDeviceOutput unit callback |
| 400 OSStatus AUAudioInputStream::InputProc(void* user_data, | 424 OSStatus AUAudioInputStream::InputProc(void* user_data, |
| 401 AudioUnitRenderActionFlags* flags, | 425 AudioUnitRenderActionFlags* flags, |
| 402 const AudioTimeStamp* time_stamp, | 426 const AudioTimeStamp* time_stamp, |
| 403 UInt32 bus_number, | 427 UInt32 bus_number, |
| 404 UInt32 number_of_frames, | 428 UInt32 number_of_frames, |
| 405 AudioBufferList* io_data) { | 429 AudioBufferList* io_data) { |
| 406 // Verify that the correct bus is used (Input bus/Element 1) | 430 // Verify that the correct bus is used (Input bus/Element 1) |
| 407 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); | 431 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); |
| 408 AUAudioInputStream* audio_input = | 432 AUAudioInputStream* audio_input = |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 434 double capture_latency_frames = GetCaptureLatency(time_stamp); | 458 double capture_latency_frames = GetCaptureLatency(time_stamp); |
| 435 | 459 |
| 436 AudioBuffer& buffer = io_data->mBuffers[0]; | 460 AudioBuffer& buffer = io_data->mBuffers[0]; |
| 437 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); | 461 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); |
| 438 uint32 capture_delay_bytes = static_cast<uint32> | 462 uint32 capture_delay_bytes = static_cast<uint32> |
| 439 ((capture_latency_frames + 0.5) * format_.mBytesPerFrame); | 463 ((capture_latency_frames + 0.5) * format_.mBytesPerFrame); |
| 440 DCHECK(audio_data); | 464 DCHECK(audio_data); |
| 441 if (!audio_data) | 465 if (!audio_data) |
| 442 return kAudioUnitErr_InvalidElement; | 466 return kAudioUnitErr_InvalidElement; |
| 443 | 467 |
| 444 sink_->OnData(this, audio_data, buffer.mDataByteSize, capture_delay_bytes); | 468 // Query a new volume level if AGC is enabled and if it is time to update |
| 469 // the old value. | |
| 470 if (GetAutomaticGainControl()) { | |
| 471 base::Time now = base::Time::Now(); | |
| 472 if ((now - last_volume_update_time_).InMilliseconds() > | |
| 473 kMinIntervalBetweenVolumeUpdatesMs) { | |
| 474 // Retrieve the current volume level. Range is [0.0,1.0]. | |
| 475 volume_ = static_cast<double>(GetVolume()); | |
| 476 DLOG(INFO) << "Volume updated to: " << volume_; | |
|
scherkus (not reviewing)
2012/03/20 13:49:41
logspam
henrika (OOO until Aug 14)
2012/03/21 10:16:04
Done.
| |
| 477 last_volume_update_time_ = now; | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 // Deliver data packet, delay estimation and volume level to the user. | |
| 482 sink_->OnData(this, | |
| 483 audio_data, | |
| 484 buffer.mDataByteSize, | |
| 485 capture_delay_bytes, | |
| 486 volume_); | |
| 445 | 487 |
| 446 return noErr; | 488 return noErr; |
| 447 } | 489 } |
| 448 | 490 |
| 449 double AUAudioInputStream::HardwareSampleRate() { | 491 double AUAudioInputStream::HardwareSampleRate() { |
| 450 // Determine the default input device's sample-rate. | 492 // Determine the default input device's sample-rate. |
| 451 AudioDeviceID device_id = kAudioObjectUnknown; | 493 AudioDeviceID device_id = kAudioObjectUnknown; |
| 452 UInt32 info_size = sizeof(device_id); | 494 UInt32 info_size = sizeof(device_id); |
| 453 | 495 |
| 454 AudioObjectPropertyAddress default_input_device_address = { | 496 AudioObjectPropertyAddress default_input_device_address = { |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 575 AudioObjectPropertyAddress property_address = { | 617 AudioObjectPropertyAddress property_address = { |
| 576 kAudioDevicePropertyVolumeScalar, | 618 kAudioDevicePropertyVolumeScalar, |
| 577 kAudioDevicePropertyScopeInput, | 619 kAudioDevicePropertyScopeInput, |
| 578 static_cast<UInt32>(channel) | 620 static_cast<UInt32>(channel) |
| 579 }; | 621 }; |
| 580 OSStatus result = AudioObjectIsPropertySettable(input_device_id_, | 622 OSStatus result = AudioObjectIsPropertySettable(input_device_id_, |
| 581 &property_address, | 623 &property_address, |
| 582 &is_settable); | 624 &is_settable); |
| 583 return (result == noErr) ? is_settable : false; | 625 return (result == noErr) ? is_settable : false; |
| 584 } | 626 } |
| OLD | NEW |