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++) { | |
275 // If the volume is settable, the valid volume range is [0.0, 1.0]. | |
henrika (OOO until Aug 14)
2012/02/23 11:50:41
How do you know that this is a true statement?
no longer working on chromium
2012/02/24 09:27:48
From Apple document:
kAudioDevicePropertyVolumeSca
| |
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; | |
290 | |
291 Float32 volume_float32 = static_cast<Float32> (volume); | |
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); | |
301 OSStatus result = AudioObjectSetPropertyData(input_device_id_, | |
302 &property_address, | |
303 0, | |
304 NULL, | |
305 size, | |
306 &volume_float32); | |
307 DLOG_IF(WARNING, result != noErr) << "SetVolume failed to set volume to " | |
308 << volume_float32; | |
309 return; | |
310 } | |
311 | |
312 // There is no master volume control, try to set volume for each channel. | |
313 int success_on_channel = 0; | |
314 for (UInt32 i = 1; i <= number_of_channels_in_frame_; i++) { | |
315 property_address.mElement = i; | |
316 if (IsVolumeSettableOnChannel(i)) { | |
317 UInt32 size = sizeof(volume_float32); | |
318 OSStatus result = AudioObjectSetPropertyData(input_device_id_, | |
319 &property_address, | |
320 0, | |
321 NULL, | |
322 size, | |
323 &volume_float32); | |
324 if (result == noErr) | |
325 ++success_on_channel; | |
326 } | |
327 } | |
328 | |
329 DLOG_IF(WARNING, success_on_channel == 0) | |
330 << "SetVolume failed to set volume to " << volume_float32; | |
331 } | |
332 | |
333 double AUAudioInputStream::GetVolume() { | |
334 // Verify that we have a valid device. | |
335 if (input_device_id_ == kAudioObjectUnknown) | |
336 return 0.0; | |
337 | |
338 AudioObjectPropertyAddress property_address = { | |
339 kAudioDevicePropertyVolumeScalar, | |
340 kAudioDevicePropertyScopeInput, | |
341 kAudioObjectPropertyElementMaster | |
342 }; | |
343 | |
344 if (AudioObjectHasProperty(input_device_id_, &property_address)) { | |
345 // The device supports master volume control, get the volume from the | |
346 // master channel. | |
347 Float32 volume_float32 = 0.0; | |
348 UInt32 size = sizeof(volume_float32); | |
349 OSStatus result = AudioObjectGetPropertyData(input_device_id_, | |
350 &property_address, | |
351 0, | |
352 NULL, | |
353 &size, | |
354 &volume_float32); | |
355 if (result == noErr) | |
356 return static_cast<double> (volume_float32); | |
357 } else { | |
358 // There is no master volume control, try to get the average volume of | |
359 // all the channels. | |
360 Float32 volume_float32 = 0.0; | |
361 int success_on_channel = 0; | |
362 for (UInt32 i = 1; i <= number_of_channels_in_frame_; i++) { | |
363 property_address.mElement = i; | |
364 if (AudioObjectHasProperty(input_device_id_, &property_address)) { | |
365 Float32 channel_volume = 0; | |
366 UInt32 size = sizeof(channel_volume); | |
367 OSStatus result = AudioObjectGetPropertyData(input_device_id_, | |
368 &property_address, | |
369 0, | |
370 NULL, | |
371 &size, | |
372 &channel_volume); | |
373 if (result == noErr) { | |
374 volume_float32 += channel_volume; | |
375 ++success_on_channel; | |
376 } | |
377 } | |
378 } | |
379 | |
380 // Get the average volume of the channels. | |
381 if (success_on_channel != 0) | |
382 return static_cast<double> (volume_float32 / success_on_channel); | |
383 } | |
384 | |
385 DLOG(WARNING) << "AudioObjectGetPropertyData failed not get volume"; | |
henrika (OOO until Aug 14)
2012/02/23 11:50:41
Clean up the comment. "failed not get volume" ...
no longer working on chromium
2012/02/24 09:27:48
Done.
| |
386 return 0.0; | |
387 } | |
388 | |
266 // AUHAL AudioDeviceOutput unit callback | 389 // AUHAL AudioDeviceOutput unit callback |
267 OSStatus AUAudioInputStream::InputProc(void* user_data, | 390 OSStatus AUAudioInputStream::InputProc(void* user_data, |
268 AudioUnitRenderActionFlags* flags, | 391 AudioUnitRenderActionFlags* flags, |
269 const AudioTimeStamp* time_stamp, | 392 const AudioTimeStamp* time_stamp, |
270 UInt32 bus_number, | 393 UInt32 bus_number, |
271 UInt32 number_of_frames, | 394 UInt32 number_of_frames, |
272 AudioBufferList* io_data) { | 395 AudioBufferList* io_data) { |
273 // Verify that the correct bus is used (Input bus/Element 1) | 396 // Verify that the correct bus is used (Input bus/Element 1) |
274 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); | 397 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); |
275 AUAudioInputStream* audio_input = | 398 AUAudioInputStream* audio_input = |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
381 UInt32 device_latency_frames = 0; | 504 UInt32 device_latency_frames = 0; |
382 size = sizeof(device_latency_frames); | 505 size = sizeof(device_latency_frames); |
383 result = AudioObjectGetPropertyData(input_device_id_, | 506 result = AudioObjectGetPropertyData(input_device_id_, |
384 &property_address, | 507 &property_address, |
385 0, | 508 0, |
386 NULL, | 509 NULL, |
387 &size, | 510 &size, |
388 &device_latency_frames); | 511 &device_latency_frames); |
389 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; | 512 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; |
390 | 513 |
514 // Get the stream latency. | |
henrika (OOO until Aug 14)
2012/02/23 11:50:41
I don't understand where this part comes from. Not
no longer working on chromium
2012/02/24 09:27:48
Sorry, merge error.
| |
515 property_address.mSelector = kAudioDevicePropertyStreams; | |
516 UInt32 stream_latency_frames = 0; | |
517 size = 0; | |
518 result = AudioObjectGetPropertyDataSize(input_device_id_, | |
519 &property_address, | |
520 0, | |
521 NULL, | |
522 &size); | |
523 if (!result) { | |
524 scoped_ptr_malloc<AudioStreamID> | |
525 streams(reinterpret_cast<AudioStreamID*>(malloc(size))); | |
526 AudioStreamID* stream_ids = streams.get(); | |
527 result = AudioObjectGetPropertyData(input_device_id_, | |
528 &property_address, | |
529 0, | |
530 NULL, | |
531 &size, | |
532 stream_ids); | |
533 if (!result) { | |
534 property_address.mSelector = kAudioStreamPropertyLatency; | |
535 result = AudioObjectGetPropertyData(stream_ids[0], | |
536 &property_address, | |
537 0, | |
538 NULL, | |
539 &size, | |
540 &stream_latency_frames); | |
541 } | |
542 } | |
543 DLOG_IF(WARNING, result != noErr) << "Could not get audio stream latency."; | |
544 | |
391 return static_cast<double>((audio_unit_latency_sec * | 545 return static_cast<double>((audio_unit_latency_sec * |
392 format_.mSampleRate) + device_latency_frames); | 546 format_.mSampleRate) + device_latency_frames + stream_latency_frames); |
393 } | 547 } |
394 | 548 |
395 double AUAudioInputStream::GetCaptureLatency( | 549 double AUAudioInputStream::GetCaptureLatency( |
396 const AudioTimeStamp* input_time_stamp) { | 550 const AudioTimeStamp* input_time_stamp) { |
397 // Get the delay between between the actual recording instant and the time | 551 // Get the delay between between the actual recording instant and the time |
398 // when the data packet is provided as a callback. | 552 // when the data packet is provided as a callback. |
399 UInt64 capture_time_ns = AudioConvertHostTimeToNanos( | 553 UInt64 capture_time_ns = AudioConvertHostTimeToNanos( |
400 input_time_stamp->mHostTime); | 554 input_time_stamp->mHostTime); |
401 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | 555 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); |
402 double delay_frames = static_cast<double> | 556 double delay_frames = static_cast<double> |
403 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); | 557 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); |
404 | 558 |
405 // Total latency is composed by the dynamic latency and the fixed | 559 // Total latency is composed by the dynamic latency and the fixed |
406 // hardware latency. | 560 // hardware latency. |
407 return (delay_frames + hardware_latency_frames_); | 561 return (delay_frames + hardware_latency_frames_); |
408 } | 562 } |
409 | 563 |
564 UInt32 AUAudioInputStream::GetNumberOfChannelsFromStream() { | |
565 // Get the stream format, to be able to read the number of channels. | |
566 AudioObjectPropertyAddress property_address = { | |
567 kAudioDevicePropertyStreamFormat, | |
568 kAudioDevicePropertyScopeInput, | |
569 kAudioObjectPropertyElementMaster | |
570 }; | |
571 AudioStreamBasicDescription stream_format; | |
572 UInt32 size = sizeof(stream_format); | |
573 OSStatus result = AudioObjectGetPropertyData(input_device_id_, | |
574 &property_address, | |
575 0, | |
576 NULL, | |
577 &size, | |
578 &stream_format); | |
579 DLOG_IF(WARNING, result != noErr) << "Could not get stream format."; | |
580 | |
581 return stream_format.mChannelsPerFrame; | |
582 } | |
583 | |
410 void AUAudioInputStream::HandleError(OSStatus err) { | 584 void AUAudioInputStream::HandleError(OSStatus err) { |
411 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) | 585 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) |
412 << " (" << err << ")"; | 586 << " (" << err << ")"; |
413 if (sink_) | 587 if (sink_) |
414 sink_->OnError(this, static_cast<int>(err)); | 588 sink_->OnError(this, static_cast<int>(err)); |
415 } | 589 } |
590 | |
591 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { | |
592 Boolean is_settable = false; | |
593 AudioObjectPropertyAddress property_address = { | |
594 kAudioDevicePropertyVolumeScalar, | |
595 kAudioDevicePropertyScopeInput, | |
596 kAudioObjectPropertyElementMaster | |
597 }; | |
598 property_address.mElement = channel; | |
599 OSStatus result = AudioObjectIsPropertySettable(input_device_id_, | |
600 &property_address, | |
601 &is_settable); | |
602 return (result == noErr) ? is_settable : false; | |
603 } | |
OLD | NEW |