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 26 matching lines...) Expand all Loading... |
77 | 78 |
78 AUAudioInputStream::~AUAudioInputStream() {} | 79 AUAudioInputStream::~AUAudioInputStream() {} |
79 | 80 |
80 // Obtain and open the AUHAL AudioOutputUnit for recording. | 81 // Obtain and open the AUHAL AudioOutputUnit for recording. |
81 bool AUAudioInputStream::Open() { | 82 bool AUAudioInputStream::Open() { |
82 // Verify that we are not already opened. | 83 // Verify that we are not already opened. |
83 if (audio_unit_) | 84 if (audio_unit_) |
84 return false; | 85 return false; |
85 | 86 |
86 // Verify that we have a valid device. | 87 // Verify that we have a valid device. |
87 if (input_device_id_ == kAudioObjectUnknown) | 88 if (input_device_id_ == kAudioObjectUnknown) { |
| 89 NOTREACHED() << "Device ID is unknown"; |
88 return false; | 90 return false; |
| 91 } |
89 | 92 |
90 // Start by obtaining an AudioOuputUnit using an AUHAL component description. | 93 // Start by obtaining an AudioOuputUnit using an AUHAL component description. |
91 | 94 |
92 Component comp; | 95 Component comp; |
93 ComponentDescription desc; | 96 ComponentDescription desc; |
94 | 97 |
95 // Description for the Audio Unit we want to use (AUHAL in this case). | 98 // Description for the Audio Unit we want to use (AUHAL in this case). |
96 desc.componentType = kAudioUnitType_Output; | 99 desc.componentType = kAudioUnitType_Output; |
97 desc.componentSubType = kAudioUnitSubType_HALOutput; | 100 desc.componentSubType = kAudioUnitSubType_HALOutput; |
98 desc.componentManufacturer = kAudioUnitManufacturer_Apple; | 101 desc.componentManufacturer = kAudioUnitManufacturer_Apple; |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
204 // it can produce in response to a single render call. | 207 // it can produce in response to a single render call. |
205 result = AudioUnitInitialize(audio_unit_); | 208 result = AudioUnitInitialize(audio_unit_); |
206 if (result) { | 209 if (result) { |
207 HandleError(result); | 210 HandleError(result); |
208 return false; | 211 return false; |
209 } | 212 } |
210 | 213 |
211 // The hardware latency is fixed and will not change during the call. | 214 // The hardware latency is fixed and will not change during the call. |
212 hardware_latency_frames_ = GetHardwareLatency(); | 215 hardware_latency_frames_ = GetHardwareLatency(); |
213 | 216 |
| 217 // The master channel is 0, Left and right are channels 1 and 2. |
| 218 // And the master channel is not counted in |number_of_channels_in_frame_|. |
| 219 number_of_channels_in_frame_ = GetNumberOfChannelsFromStream(); |
| 220 |
214 return true; | 221 return true; |
215 } | 222 } |
216 | 223 |
217 void AUAudioInputStream::Start(AudioInputCallback* callback) { | 224 void AUAudioInputStream::Start(AudioInputCallback* callback) { |
218 DCHECK(callback); | 225 DCHECK(callback); |
219 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully"; | 226 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully"; |
220 if (started_ || !audio_unit_) | 227 if (started_ || !audio_unit_) |
221 return; | 228 return; |
222 sink_ = callback; | 229 sink_ = callback; |
223 OSStatus result = AudioOutputUnitStart(audio_unit_); | 230 OSStatus result = AudioOutputUnitStart(audio_unit_); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 if (sink_) { | 263 if (sink_) { |
257 sink_->OnClose(this); | 264 sink_->OnClose(this); |
258 sink_ = NULL; | 265 sink_ = NULL; |
259 } | 266 } |
260 | 267 |
261 // Inform the audio manager that we have been closed. This can cause our | 268 // Inform the audio manager that we have been closed. This can cause our |
262 // destruction. | 269 // destruction. |
263 manager_->ReleaseInputStream(this); | 270 manager_->ReleaseInputStream(this); |
264 } | 271 } |
265 | 272 |
| 273 double AUAudioInputStream::GetMaxVolume() { |
| 274 // Verify that we have a valid device. |
| 275 if (input_device_id_ == kAudioObjectUnknown) { |
| 276 NOTREACHED() << "Device ID is unknown"; |
| 277 return 0.0; |
| 278 } |
| 279 |
| 280 // Query if any of the master, left or right channels has volume control. |
| 281 for (int i = 0; i <= number_of_channels_in_frame_; ++i) { |
| 282 // If the volume is settable, the valid volume range is [0.0, 1.0]. |
| 283 if (IsVolumeSettableOnChannel(i)) |
| 284 return 1.0; |
| 285 } |
| 286 |
| 287 // Volume control is not available for the audio stream. |
| 288 return 0.0; |
| 289 } |
| 290 |
| 291 void AUAudioInputStream::SetVolume(double volume) { |
| 292 DCHECK(volume <= 1.0 && volume >= 0.0); |
| 293 |
| 294 // Verify that we have a valid device. |
| 295 if (input_device_id_ == kAudioObjectUnknown) { |
| 296 NOTREACHED() << "Device ID is unknown"; |
| 297 return; |
| 298 } |
| 299 |
| 300 Float32 volume_float32 = static_cast<Float32>(volume); |
| 301 AudioObjectPropertyAddress property_address = { |
| 302 kAudioDevicePropertyVolumeScalar, |
| 303 kAudioDevicePropertyScopeInput, |
| 304 kAudioObjectPropertyElementMaster |
| 305 }; |
| 306 |
| 307 // Try to set the volume for master volume channel. |
| 308 if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) { |
| 309 OSStatus result = AudioObjectSetPropertyData(input_device_id_, |
| 310 &property_address, |
| 311 0, |
| 312 NULL, |
| 313 sizeof(volume_float32), |
| 314 &volume_float32); |
| 315 if (result != noErr) { |
| 316 DLOG(WARNING) << "Failed to set volume to " << volume_float32; |
| 317 } |
| 318 return; |
| 319 } |
| 320 |
| 321 // There is no master volume control, try to set volume for each channel. |
| 322 int successful_channels = 0; |
| 323 for (int i = 1; i <= number_of_channels_in_frame_; ++i) { |
| 324 property_address.mElement = static_cast<UInt32>(i); |
| 325 if (IsVolumeSettableOnChannel(i)) { |
| 326 OSStatus result = AudioObjectSetPropertyData(input_device_id_, |
| 327 &property_address, |
| 328 0, |
| 329 NULL, |
| 330 sizeof(volume_float32), |
| 331 &volume_float32); |
| 332 if (result == noErr) |
| 333 ++successful_channels; |
| 334 } |
| 335 } |
| 336 |
| 337 DLOG_IF(WARNING, successful_channels == 0) |
| 338 << "Failed to set volume to " << volume_float32; |
| 339 } |
| 340 |
| 341 double AUAudioInputStream::GetVolume() { |
| 342 // Verify that we have a valid device. |
| 343 if (input_device_id_ == kAudioObjectUnknown){ |
| 344 NOTREACHED() << "Device ID is unknown"; |
| 345 return 0.0; |
| 346 } |
| 347 |
| 348 AudioObjectPropertyAddress property_address = { |
| 349 kAudioDevicePropertyVolumeScalar, |
| 350 kAudioDevicePropertyScopeInput, |
| 351 kAudioObjectPropertyElementMaster |
| 352 }; |
| 353 |
| 354 if (AudioObjectHasProperty(input_device_id_, &property_address)) { |
| 355 // The device supports master volume control, get the volume from the |
| 356 // master channel. |
| 357 Float32 volume_float32 = 0.0; |
| 358 UInt32 size = sizeof(volume_float32); |
| 359 OSStatus result = AudioObjectGetPropertyData(input_device_id_, |
| 360 &property_address, |
| 361 0, |
| 362 NULL, |
| 363 &size, |
| 364 &volume_float32); |
| 365 if (result == noErr) |
| 366 return static_cast<double>(volume_float32); |
| 367 } else { |
| 368 // There is no master volume control, try to get the average volume of |
| 369 // all the channels. |
| 370 Float32 volume_float32 = 0.0; |
| 371 int successful_channels = 0; |
| 372 for (int i = 1; i <= number_of_channels_in_frame_; ++i) { |
| 373 property_address.mElement = static_cast<UInt32>(i); |
| 374 if (AudioObjectHasProperty(input_device_id_, &property_address)) { |
| 375 Float32 channel_volume = 0; |
| 376 UInt32 size = sizeof(channel_volume); |
| 377 OSStatus result = AudioObjectGetPropertyData(input_device_id_, |
| 378 &property_address, |
| 379 0, |
| 380 NULL, |
| 381 &size, |
| 382 &channel_volume); |
| 383 if (result == noErr) { |
| 384 volume_float32 += channel_volume; |
| 385 ++successful_channels; |
| 386 } |
| 387 } |
| 388 } |
| 389 |
| 390 // Get the average volume of the channels. |
| 391 if (successful_channels != 0) |
| 392 return static_cast<double>(volume_float32 / successful_channels); |
| 393 } |
| 394 |
| 395 DLOG(WARNING) << "Failed to get volume"; |
| 396 return 0.0; |
| 397 } |
| 398 |
266 // AUHAL AudioDeviceOutput unit callback | 399 // AUHAL AudioDeviceOutput unit callback |
267 OSStatus AUAudioInputStream::InputProc(void* user_data, | 400 OSStatus AUAudioInputStream::InputProc(void* user_data, |
268 AudioUnitRenderActionFlags* flags, | 401 AudioUnitRenderActionFlags* flags, |
269 const AudioTimeStamp* time_stamp, | 402 const AudioTimeStamp* time_stamp, |
270 UInt32 bus_number, | 403 UInt32 bus_number, |
271 UInt32 number_of_frames, | 404 UInt32 number_of_frames, |
272 AudioBufferList* io_data) { | 405 AudioBufferList* io_data) { |
273 // Verify that the correct bus is used (Input bus/Element 1) | 406 // Verify that the correct bus is used (Input bus/Element 1) |
274 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); | 407 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); |
275 AUAudioInputStream* audio_input = | 408 AUAudioInputStream* audio_input = |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 input_time_stamp->mHostTime); | 533 input_time_stamp->mHostTime); |
401 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | 534 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); |
402 double delay_frames = static_cast<double> | 535 double delay_frames = static_cast<double> |
403 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); | 536 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); |
404 | 537 |
405 // Total latency is composed by the dynamic latency and the fixed | 538 // Total latency is composed by the dynamic latency and the fixed |
406 // hardware latency. | 539 // hardware latency. |
407 return (delay_frames + hardware_latency_frames_); | 540 return (delay_frames + hardware_latency_frames_); |
408 } | 541 } |
409 | 542 |
| 543 int AUAudioInputStream::GetNumberOfChannelsFromStream() { |
| 544 // Get the stream format, to be able to read the number of channels. |
| 545 AudioObjectPropertyAddress property_address = { |
| 546 kAudioDevicePropertyStreamFormat, |
| 547 kAudioDevicePropertyScopeInput, |
| 548 kAudioObjectPropertyElementMaster |
| 549 }; |
| 550 AudioStreamBasicDescription stream_format; |
| 551 UInt32 size = sizeof(stream_format); |
| 552 OSStatus result = AudioObjectGetPropertyData(input_device_id_, |
| 553 &property_address, |
| 554 0, |
| 555 NULL, |
| 556 &size, |
| 557 &stream_format); |
| 558 if (result != noErr) { |
| 559 DLOG(WARNING) << "Could not get stream format"; |
| 560 return 0; |
| 561 } |
| 562 |
| 563 return static_cast<int>(stream_format.mChannelsPerFrame); |
| 564 } |
| 565 |
410 void AUAudioInputStream::HandleError(OSStatus err) { | 566 void AUAudioInputStream::HandleError(OSStatus err) { |
411 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) | 567 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) |
412 << " (" << err << ")"; | 568 << " (" << err << ")"; |
413 if (sink_) | 569 if (sink_) |
414 sink_->OnError(this, static_cast<int>(err)); | 570 sink_->OnError(this, static_cast<int>(err)); |
415 } | 571 } |
| 572 |
| 573 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { |
| 574 Boolean is_settable = false; |
| 575 AudioObjectPropertyAddress property_address = { |
| 576 kAudioDevicePropertyVolumeScalar, |
| 577 kAudioDevicePropertyScopeInput, |
| 578 static_cast<UInt32>(channel) |
| 579 }; |
| 580 OSStatus result = AudioObjectIsPropertySettable(input_device_id_, |
| 581 &property_address, |
| 582 &is_settable); |
| 583 return (result == noErr) ? is_settable : false; |
| 584 } |
OLD | NEW |