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" |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 30 // for more details and background regarding this implementation. | 30 // for more details and background regarding this implementation. |
| 31 | 31 |
| 32 AUAudioInputStream::AUAudioInputStream( | 32 AUAudioInputStream::AUAudioInputStream( |
| 33 AudioManagerMac* manager, const AudioParameters& params, | 33 AudioManagerMac* manager, const AudioParameters& params, |
| 34 AudioDeviceID audio_device_id) | 34 AudioDeviceID audio_device_id) |
| 35 : manager_(manager), | 35 : manager_(manager), |
| 36 sink_(NULL), | 36 sink_(NULL), |
| 37 audio_unit_(0), | 37 audio_unit_(0), |
| 38 input_device_id_(audio_device_id), | 38 input_device_id_(audio_device_id), |
| 39 started_(false), | 39 started_(false), |
| 40 hardware_latency_frames_(0) { | 40 hardware_latency_frames_(0), |
| 41 number_of_channels_in_frame_(0) { | |
| 41 DCHECK(manager_); | 42 DCHECK(manager_); |
| 42 | 43 |
| 43 // Set up the desired (output) format specified by the client. | 44 // Set up the desired (output) format specified by the client. |
| 44 format_.mSampleRate = params.sample_rate; | 45 format_.mSampleRate = params.sample_rate; |
| 45 format_.mFormatID = kAudioFormatLinearPCM; | 46 format_.mFormatID = kAudioFormatLinearPCM; |
| 46 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 47 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
| 47 kLinearPCMFormatFlagIsSignedInteger; | 48 kLinearPCMFormatFlagIsSignedInteger; |
| 48 format_.mBitsPerChannel = params.bits_per_sample; | 49 format_.mBitsPerChannel = params.bits_per_sample; |
| 49 format_.mChannelsPerFrame = params.channels; | 50 format_.mChannelsPerFrame = params.channels; |
| 50 format_.mFramesPerPacket = 1; // uncompressed audio | 51 format_.mFramesPerPacket = 1; // uncompressed audio |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 204 // it can produce in response to a single render call. | 205 // it can produce in response to a single render call. |
| 205 result = AudioUnitInitialize(audio_unit_); | 206 result = AudioUnitInitialize(audio_unit_); |
| 206 if (result) { | 207 if (result) { |
| 207 HandleError(result); | 208 HandleError(result); |
| 208 return false; | 209 return false; |
| 209 } | 210 } |
| 210 | 211 |
| 211 // The hardware latency is fixed and will not change during the call. | 212 // The hardware latency is fixed and will not change during the call. |
| 212 hardware_latency_frames_ = GetHardwareLatency(); | 213 hardware_latency_frames_ = GetHardwareLatency(); |
| 213 | 214 |
| 215 number_of_channels_in_frame_ = GetNumberOfChannelsFromStream(); | |
| 216 | |
| 214 return true; | 217 return true; |
| 215 } | 218 } |
| 216 | 219 |
| 217 void AUAudioInputStream::Start(AudioInputCallback* callback) { | 220 void AUAudioInputStream::Start(AudioInputCallback* callback) { |
| 218 DCHECK(callback); | 221 DCHECK(callback); |
| 219 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully"; | 222 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully"; |
| 220 if (started_ || !audio_unit_) | 223 if (started_ || !audio_unit_) |
| 221 return; | 224 return; |
| 222 sink_ = callback; | 225 sink_ = callback; |
| 223 OSStatus result = AudioOutputUnitStart(audio_unit_); | 226 OSStatus result = AudioOutputUnitStart(audio_unit_); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 256 if (sink_) { | 259 if (sink_) { |
| 257 sink_->OnClose(this); | 260 sink_->OnClose(this); |
| 258 sink_ = NULL; | 261 sink_ = NULL; |
| 259 } | 262 } |
| 260 | 263 |
| 261 // Inform the audio manager that we have been closed. This can cause our | 264 // Inform the audio manager that we have been closed. This can cause our |
| 262 // destruction. | 265 // destruction. |
| 263 manager_->ReleaseInputStream(this); | 266 manager_->ReleaseInputStream(this); |
| 264 } | 267 } |
| 265 | 268 |
| 269 double AUAudioInputStream::GetMaxVolume() { | |
| 270 // Verify that we have a valid device. | |
| 271 if (input_device_id_ == kAudioObjectUnknown) | |
| 272 return 0.0; | |
| 273 | |
| 274 for (UInt32 i = 0; i <= number_of_channels_in_frame_; i++) { | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
++i
tommi (sloooow) - chröme
2012/02/24 12:07:34
is the condition correct? Shouldn't it be 'i < nu
tommi (sloooow) - chröme
2012/02/24 12:07:34
Please clean up all the unnecessary UInt32 (and po
no longer working on chromium
2012/02/24 15:05:55
I think it is correct. For example number_of_chann
no longer working on chromium
2012/02/24 15:05:55
Done.
no longer working on chromium
2012/02/24 15:05:55
Done.
| |
| 275 // If the volume is settable, the valid volume range is [0.0, 1.0]. | |
| 276 if (IsVolumeSettableOnChannel(i)) | |
| 277 return 1.0; | |
| 278 } | |
| 279 | |
| 280 // Volume control is not available for the audio stream. | |
| 281 return 0.0; | |
| 282 } | |
| 283 | |
| 284 void AUAudioInputStream::SetVolume(double volume) { | |
| 285 DCHECK(volume <= 1.0 && volume >= 0.0); | |
| 286 | |
| 287 // Verify that we have a valid device. | |
| 288 if (input_device_id_ == kAudioObjectUnknown) | |
| 289 return; | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
NOTREACHED?
no longer working on chromium
2012/02/24 15:05:55
Done.
| |
| 290 | |
| 291 Float32 volume_float32 = static_cast<Float32> (volume); | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
no space between >(
no longer working on chromium
2012/02/24 15:05:55
Done.
| |
| 292 AudioObjectPropertyAddress property_address = { | |
| 293 kAudioDevicePropertyVolumeScalar, | |
| 294 kAudioDevicePropertyScopeInput, | |
| 295 kAudioObjectPropertyElementMaster | |
| 296 }; | |
| 297 | |
| 298 // Try to set the volume for master volume channel. | |
| 299 if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) { | |
| 300 UInt32 size = sizeof(volume_float32); | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
nit: this variable isn't necessary. Just pass size
no longer working on chromium
2012/02/24 15:05:55
Done.
| |
| 301 OSStatus result = AudioObjectSetPropertyData(input_device_id_, | |
| 302 &property_address, | |
| 303 0, | |
| 304 NULL, | |
| 305 size, | |
| 306 &volume_float32); | |
| 307 if (result != noErr) { | |
| 308 DLOG(WARNING) << "Failed to set volume to " << volume_float32; | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
use DLOG_IF instead
no longer working on chromium
2012/02/24 15:05:55
As discussed, keep it to get rid of the "unused va
| |
| 309 } | |
| 310 return; | |
| 311 } | |
| 312 | |
| 313 // There is no master volume control, try to set volume for each channel. | |
| 314 int success_on_channel = 0; | |
| 315 for (UInt32 i = 1; i <= number_of_channels_in_frame_; i++) { | |
| 316 property_address.mElement = i; | |
| 317 if (IsVolumeSettableOnChannel(i)) { | |
| 318 UInt32 size = sizeof(volume_float32); | |
| 319 OSStatus result = AudioObjectSetPropertyData(input_device_id_, | |
| 320 &property_address, | |
| 321 0, | |
| 322 NULL, | |
| 323 size, | |
| 324 &volume_float32); | |
| 325 if (result == noErr) | |
| 326 ++success_on_channel; | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 DLOG_IF(WARNING, success_on_channel == 0) | |
| 331 << "Failed to set volume to " << volume_float32; | |
| 332 } | |
| 333 | |
| 334 double AUAudioInputStream::GetVolume() { | |
| 335 // Verify that we have a valid device. | |
| 336 if (input_device_id_ == kAudioObjectUnknown) | |
| 337 return 0.0; | |
| 338 | |
| 339 AudioObjectPropertyAddress property_address = { | |
| 340 kAudioDevicePropertyVolumeScalar, | |
| 341 kAudioDevicePropertyScopeInput, | |
| 342 kAudioObjectPropertyElementMaster | |
| 343 }; | |
| 344 | |
| 345 if (AudioObjectHasProperty(input_device_id_, &property_address)) { | |
| 346 // The device supports master volume control, get the volume from the | |
| 347 // master channel. | |
| 348 Float32 volume_float32 = 0.0; | |
| 349 UInt32 size = sizeof(volume_float32); | |
| 350 OSStatus result = AudioObjectGetPropertyData(input_device_id_, | |
| 351 &property_address, | |
| 352 0, | |
| 353 NULL, | |
| 354 &size, | |
| 355 &volume_float32); | |
| 356 if (result == noErr) | |
| 357 return static_cast<double> (volume_float32); | |
| 358 } else { | |
| 359 // There is no master volume control, try to get the average volume of | |
| 360 // all the channels. | |
| 361 Float32 volume_float32 = 0.0; | |
| 362 int success_on_channel = 0; | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
successful_channels?
no longer working on chromium
2012/02/24 15:05:55
Done.
| |
| 363 for (UInt32 i = 1; i <= number_of_channels_in_frame_; i++) { | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
++i
please fix throughout
no longer working on chromium
2012/02/24 15:05:55
Done.
| |
| 364 property_address.mElement = i; | |
| 365 if (AudioObjectHasProperty(input_device_id_, &property_address)) { | |
| 366 Float32 channel_volume = 0; | |
| 367 UInt32 size = sizeof(channel_volume); | |
| 368 OSStatus result = AudioObjectGetPropertyData(input_device_id_, | |
| 369 &property_address, | |
| 370 0, | |
| 371 NULL, | |
| 372 &size, | |
| 373 &channel_volume); | |
| 374 if (result == noErr) { | |
| 375 volume_float32 += channel_volume; | |
| 376 ++success_on_channel; | |
| 377 } | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 // Get the average volume of the channels. | |
| 382 if (success_on_channel != 0) | |
| 383 return static_cast<double> (volume_float32 / success_on_channel); | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
no space between >(
please fix throughout
no longer working on chromium
2012/02/24 15:05:55
Done.
| |
| 384 } | |
| 385 | |
| 386 DLOG(WARNING) << "Failed not get volume"; | |
|
tommi (sloooow) - chröme
2012/02/24 12:07:34
-> "Failed to get volume"
no longer working on chromium
2012/02/24 15:05:55
Done.
| |
| 387 return 0.0; | |
| 388 } | |
| 389 | |
| 266 // AUHAL AudioDeviceOutput unit callback | 390 // AUHAL AudioDeviceOutput unit callback |
| 267 OSStatus AUAudioInputStream::InputProc(void* user_data, | 391 OSStatus AUAudioInputStream::InputProc(void* user_data, |
| 268 AudioUnitRenderActionFlags* flags, | 392 AudioUnitRenderActionFlags* flags, |
| 269 const AudioTimeStamp* time_stamp, | 393 const AudioTimeStamp* time_stamp, |
| 270 UInt32 bus_number, | 394 UInt32 bus_number, |
| 271 UInt32 number_of_frames, | 395 UInt32 number_of_frames, |
| 272 AudioBufferList* io_data) { | 396 AudioBufferList* io_data) { |
| 273 // Verify that the correct bus is used (Input bus/Element 1) | 397 // Verify that the correct bus is used (Input bus/Element 1) |
| 274 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); | 398 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); |
| 275 AUAudioInputStream* audio_input = | 399 AUAudioInputStream* audio_input = |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 400 input_time_stamp->mHostTime); | 524 input_time_stamp->mHostTime); |
| 401 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | 525 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); |
| 402 double delay_frames = static_cast<double> | 526 double delay_frames = static_cast<double> |
| 403 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); | 527 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); |
| 404 | 528 |
| 405 // Total latency is composed by the dynamic latency and the fixed | 529 // Total latency is composed by the dynamic latency and the fixed |
| 406 // hardware latency. | 530 // hardware latency. |
| 407 return (delay_frames + hardware_latency_frames_); | 531 return (delay_frames + hardware_latency_frames_); |
| 408 } | 532 } |
| 409 | 533 |
| 534 UInt32 AUAudioInputStream::GetNumberOfChannelsFromStream() { | |
| 535 // Get the stream format, to be able to read the number of channels. | |
| 536 AudioObjectPropertyAddress property_address = { | |
| 537 kAudioDevicePropertyStreamFormat, | |
| 538 kAudioDevicePropertyScopeInput, | |
| 539 kAudioObjectPropertyElementMaster | |
| 540 }; | |
| 541 AudioStreamBasicDescription stream_format; | |
| 542 UInt32 size = sizeof(stream_format); | |
| 543 OSStatus result = AudioObjectGetPropertyData(input_device_id_, | |
| 544 &property_address, | |
| 545 0, | |
| 546 NULL, | |
| 547 &size, | |
| 548 &stream_format); | |
| 549 if (result != noErr) { | |
| 550 DLOG(WARNING) << "Could not get stream format"; | |
| 551 return 0; | |
| 552 } | |
| 553 | |
| 554 return stream_format.mChannelsPerFrame; | |
| 555 } | |
| 556 | |
| 410 void AUAudioInputStream::HandleError(OSStatus err) { | 557 void AUAudioInputStream::HandleError(OSStatus err) { |
| 411 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) | 558 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) |
| 412 << " (" << err << ")"; | 559 << " (" << err << ")"; |
| 413 if (sink_) | 560 if (sink_) |
| 414 sink_->OnError(this, static_cast<int>(err)); | 561 sink_->OnError(this, static_cast<int>(err)); |
| 415 } | 562 } |
| 563 | |
| 564 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { | |
| 565 Boolean is_settable = false; | |
| 566 AudioObjectPropertyAddress property_address = { | |
| 567 kAudioDevicePropertyVolumeScalar, | |
| 568 kAudioDevicePropertyScopeInput, | |
| 569 kAudioObjectPropertyElementMaster | |
| 570 }; | |
| 571 property_address.mElement = channel; | |
| 572 OSStatus result = AudioObjectIsPropertySettable(input_device_id_, | |
| 573 &property_address, | |
| 574 &is_settable); | |
| 575 return (result == noErr) ? is_settable : false; | |
| 576 } | |
| OLD | NEW |