Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/audio/mac/audio_unified_mac.h" | |
| 6 | |
| 7 #include <CoreServices/CoreServices.h> | |
| 8 | |
| 9 #include "base/basictypes.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/mac/mac_logging.h" | |
| 12 #include "media/audio/audio_util.h" | |
| 13 #include "media/audio/mac/audio_manager_mac.h" | |
| 14 | |
| 15 // !!!! For testing only. | |
| 16 // Set to 4 to test headphone output on Stanton FinalScratch | |
| 17 #define DEST_CHANNEL_OFFSET 0 | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
can we remove this or is there some other way to a
Chris Rogers
2012/09/10 22:19:06
Yes, this was only for testing -- removed!
| |
| 18 | |
| 19 // TODO(crogers): support more than hard-coded stereo input. | |
| 20 // Ideally we would like to receive this value as a constructor argument. | |
| 21 const int kDefaultInputChannels = 2; | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
static
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 22 | |
| 23 namespace media { | |
| 24 | |
| 25 AudioHardwareUnifiedStream::AudioHardwareUnifiedStream( | |
| 26 AudioManagerMac* manager, const AudioParameters& params) | |
| 27 : manager_(manager), | |
| 28 source_(NULL), | |
| 29 volume_(1), | |
|
no longer working on chromium
2012/09/10 12:14:59
nit, 1.0f?
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 30 input_channels_(0), | |
| 31 output_channels_(0), | |
| 32 input_channels_per_frame_(0), | |
| 33 output_channels_per_frame_(0), | |
| 34 io_proc_id_(0), | |
| 35 device_(kAudioObjectUnknown), | |
| 36 is_playing_(false) { | |
| 37 // We must have a manager. | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
nit: remove comment (not adding any value)
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 38 DCHECK(manager_); | |
| 39 // A frame is one sample across all channels. In interleaved audio the per | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
nit: blank lines before new // comment blocks
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 40 // frame fields identify the set of n |channels|. In uncompressed audio, a | |
| 41 // packet is always one frame. | |
| 42 format_.mSampleRate = params.sample_rate(); | |
| 43 format_.mFormatID = kAudioFormatLinearPCM; | |
| 44 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | |
| 45 kLinearPCMFormatFlagIsSignedInteger; | |
| 46 format_.mBitsPerChannel = params.bits_per_sample(); | |
| 47 format_.mChannelsPerFrame = params.channels(); | |
| 48 format_.mFramesPerPacket = 1; | |
| 49 format_.mBytesPerPacket = (format_.mBitsPerChannel * params.channels()) / 8; | |
| 50 format_.mBytesPerFrame = format_.mBytesPerPacket; | |
| 51 format_.mReserved = 0; | |
| 52 | |
| 53 // Calculate the number of sample frames per callback. | |
| 54 number_of_frames_ = params.GetBytesPerBuffer() / format_.mBytesPerPacket; | |
| 55 | |
| 56 client_input_channels_ = kDefaultInputChannels; | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
looks like we can move this into ctor initializer
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 57 | |
| 58 input_bus_ = AudioBus::Create(client_input_channels_, | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
move client_input_channels_ to next line w/ 4 spac
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 59 params.frames_per_buffer()); | |
| 60 output_bus_ = AudioBus::Create(params); | |
| 61 } | |
| 62 | |
| 63 AudioHardwareUnifiedStream::~AudioHardwareUnifiedStream() { | |
| 64 Stop(); | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
fix indent (should be 2 spaces)
no longer working on chromium
2012/09/10 12:14:59
It looks like a programming error if the clients d
Chris Rogers
2012/09/10 22:19:06
Removed, and added DCHECK
| |
| 65 } | |
| 66 | |
| 67 bool AudioHardwareUnifiedStream::Open() { | |
| 68 device_ = kAudioDeviceUnknown; | |
|
no longer working on chromium
2012/09/10 12:14:59
nit, device_ has been initialized in the construct
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 69 | |
| 70 // Obtain the current output device selected by the user. | |
| 71 AudioObjectPropertyAddress pa; | |
| 72 pa.mSelector = kAudioHardwarePropertyDefaultOutputDevice; | |
| 73 pa.mScope = kAudioObjectPropertyScopeGlobal; | |
| 74 pa.mElement = kAudioObjectPropertyElementMaster; | |
| 75 | |
| 76 UInt32 size = sizeof(device_); | |
| 77 | |
| 78 OSStatus result = AudioObjectGetPropertyData( | |
| 79 kAudioObjectSystemObject, | |
| 80 &pa, | |
| 81 0, | |
| 82 0, | |
| 83 &size, | |
| 84 &device_); | |
| 85 | |
| 86 if ((result != kAudioHardwareNoError) || (device_ == kAudioDeviceUnknown)) { | |
| 87 LOG(ERROR) << "Cannot open unified AudioDevice."; | |
| 88 return false; | |
| 89 } | |
| 90 | |
| 91 // The requested sample-rate must match the hardware sample-rate. | |
| 92 Float64 sample_rate = 0.0; | |
| 93 size = sizeof(sample_rate); | |
| 94 | |
| 95 pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |
| 96 pa.mScope = kAudioObjectPropertyScopeWildcard; | |
| 97 pa.mElement = kAudioObjectPropertyElementMaster; | |
| 98 | |
| 99 result = AudioObjectGetPropertyData( | |
| 100 device_, | |
| 101 &pa, | |
| 102 0, | |
| 103 0, | |
| 104 &size, | |
| 105 &sample_rate); | |
| 106 | |
| 107 if (result != noErr || sample_rate != format_.mSampleRate) { | |
| 108 LOG(ERROR) << "Requested sample-rate must match the hardware sample-rate."; | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
nit: can you emit sample the two sample rates?
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 109 return false; | |
| 110 } | |
| 111 | |
| 112 // Configure buffer frame size. | |
| 113 UInt32 frame_size = number_of_frames_; | |
| 114 | |
| 115 pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |
| 116 pa.mScope = kAudioDevicePropertyScopeInput; | |
| 117 pa.mElement = kAudioObjectPropertyElementMaster; | |
| 118 result = AudioObjectSetPropertyData(device_, &pa, 0, 0, | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
nit: fix indent style -- should either look like t
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 119 sizeof(frame_size), &frame_size); | |
| 120 | |
|
no longer working on chromium
2012/09/10 12:14:59
add a DCHECK here to verify the result?
Chris Rogers
2012/09/10 22:19:06
Fixed: I'm now logging an error here and returning
| |
| 121 pa.mScope = kAudioDevicePropertyScopeOutput; | |
| 122 result = AudioObjectSetPropertyData(device_, &pa, 0, 0, | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
ditto
no longer working on chromium
2012/09/10 12:14:59
ditto
Chris Rogers
2012/09/10 22:19:06
Done.
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 123 sizeof(frame_size), &frame_size); | |
| 124 | |
| 125 DVLOG(1) << "Sample rate: " << sample_rate; | |
| 126 DVLOG(1) << "Frame size: " << frame_size; | |
| 127 | |
| 128 // Determine the number of input and output channels. | |
| 129 // We handle both the interleaved and non-interleaved cases. | |
| 130 | |
| 131 // Get input stream configuration. | |
| 132 pa.mSelector = kAudioDevicePropertyStreamConfiguration; | |
| 133 pa.mScope = kAudioDevicePropertyScopeInput; | |
| 134 pa.mElement = kAudioObjectPropertyElementMaster; | |
| 135 | |
| 136 // Allocate storage. | |
| 137 result = AudioObjectGetPropertyDataSize(device_, &pa, 0, 0, &size); | |
|
no longer working on chromium
2012/09/10 12:14:59
We are not sure if the machine has input device or
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 138 scoped_array<uint8> input_list_storage(new uint8[size]); | |
| 139 AudioBufferList& input_list = | |
| 140 *reinterpret_cast<AudioBufferList*>(input_list_storage.get()); | |
| 141 | |
| 142 result = AudioObjectGetPropertyData(device_, &pa, 0, 0, &size, &input_list); | |
|
no longer working on chromium
2012/09/10 12:14:59
DCHECK(result) ?
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 143 | |
| 144 // Determine number of input channels. | |
| 145 input_channels_per_frame_ = input_list.mNumberBuffers > 0 ? | |
| 146 input_list.mBuffers[0].mNumberChannels : 0; | |
| 147 if (input_channels_per_frame_ == 1 && input_list.mNumberBuffers > 1) { | |
|
no longer working on chromium
2012/09/10 12:14:59
We don't need to check input_list.mNumberBuffers >
Chris Rogers
2012/09/10 22:19:06
I think it's better to be more explicit since this
| |
| 148 // Non-interleaved. | |
| 149 input_channels_ = input_list.mNumberBuffers; | |
| 150 } else { | |
| 151 // Interleaved. | |
| 152 input_channels_ = input_channels_per_frame_; | |
| 153 } | |
| 154 | |
| 155 DVLOG(1) << "Input channels: " << input_channels_; | |
| 156 DVLOG(1) << "Input channels per frame: " << input_channels_per_frame_; | |
| 157 | |
| 158 // The hardware must have at least the requested input channels. | |
| 159 if (client_input_channels_ > input_channels_) { | |
| 160 LOG(ERROR) << "AudioDevice does not support requested input channels."; | |
| 161 return false; | |
| 162 } | |
| 163 | |
| 164 // Get output stream configuration. | |
| 165 pa.mSelector = kAudioDevicePropertyStreamConfiguration; | |
| 166 pa.mScope = kAudioDevicePropertyScopeOutput; | |
| 167 pa.mElement = kAudioObjectPropertyElementMaster; | |
| 168 | |
| 169 // Allocate storage. | |
| 170 AudioObjectGetPropertyDataSize(device_, &pa, 0, 0, &size); | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
do we need to check the return value of these call
no longer working on chromium
2012/09/10 12:14:59
DCHECK the size bigger than 0
Chris Rogers
2012/09/10 22:19:06
Done.
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 171 scoped_array<uint8> output_list_storage(new uint8[size]); | |
| 172 AudioBufferList& output_list = | |
| 173 *reinterpret_cast<AudioBufferList*>(output_list_storage.get()); | |
| 174 | |
| 175 AudioObjectGetPropertyData(device_, &pa, 0, 0, &size, &output_list); | |
| 176 | |
| 177 // Determine number of output channels. | |
| 178 output_channels_per_frame_ = output_list.mBuffers[0].mNumberChannels; | |
| 179 if (output_channels_per_frame_ == 1 && output_list.mNumberBuffers > 1) { | |
|
no longer working on chromium
2012/09/10 12:14:59
no need to check output_list.mNumberBuffers > 1
Chris Rogers
2012/09/10 22:19:06
I just want to be more explicit here.
| |
| 180 // Non-interleaved. | |
| 181 output_channels_ = output_list.mNumberBuffers; | |
| 182 } else { | |
| 183 // Interleaved. | |
| 184 output_channels_ = output_channels_per_frame_; | |
| 185 } | |
| 186 | |
| 187 DVLOG(1) << "Output channels: " << output_channels_; | |
| 188 DVLOG(1) << "Output channels per frame: " << output_channels_per_frame_; | |
| 189 | |
| 190 // The hardware must have at least the requested output channels. | |
| 191 if (output_channels_ < format_.mChannelsPerFrame) { | |
| 192 LOG(ERROR) << "AudioDevice does not support requested output channels."; | |
| 193 return false; | |
| 194 } | |
| 195 | |
| 196 // Setup the I/O proc. | |
| 197 result = AudioDeviceCreateIOProcID(device_, RenderProc, this, &io_proc_id_); | |
| 198 if (result != noErr) { | |
| 199 LOG(ERROR) << "Error creating IOProc."; | |
| 200 return false; | |
| 201 } | |
| 202 | |
| 203 return true; | |
| 204 } | |
| 205 | |
| 206 void AudioHardwareUnifiedStream::Close() { | |
| 207 Stop(); // make sure to stop if the client forgot. | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
Can we treat this as a programmer error / DCHECK()
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 208 | |
| 209 AudioDeviceDestroyIOProcID(device_, io_proc_id_); | |
|
no longer working on chromium
2012/09/10 12:14:59
set device_ to kAudioObjectUnknown
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 210 io_proc_id_ = 0; | |
| 211 | |
| 212 // Inform the audio manager that we have been closed. This can cause our | |
| 213 // destruction. | |
| 214 manager_->ReleaseOutputStream(this); | |
| 215 } | |
| 216 | |
| 217 void AudioHardwareUnifiedStream::Start(AudioSourceCallback* callback) { | |
| 218 DCHECK(callback); | |
| 219 DLOG_IF(ERROR, (device_ == kAudioObjectUnknown)) | |
| 220 << "Open() has not been called successfully"; | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
DCHECK() instead?
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 221 | |
| 222 if (device_ == kAudioObjectUnknown || is_playing_) | |
| 223 return; | |
| 224 | |
| 225 source_ = callback; | |
| 226 | |
| 227 // // Allocate storage for our audio source. | |
| 228 // if (input_channels_) | |
| 229 // input_data_.reset(new int16[number_of_frames_ * input_channels_]); | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
???
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 230 // output_data_.reset(new int16[number_of_frames_ * output_channels_]); | |
|
no longer working on chromium
2012/09/10 12:14:59
looks like debugging code, remove?
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 231 | |
| 232 OSStatus result = AudioDeviceStart(device_, io_proc_id_); | |
| 233 if (result == noErr) | |
| 234 is_playing_ = true; | |
| 235 } | |
| 236 | |
| 237 void AudioHardwareUnifiedStream::Stop() { | |
| 238 if (!is_playing_) | |
| 239 return; | |
| 240 | |
| 241 source_ = NULL; | |
| 242 | |
| 243 if (device_ != kAudioObjectUnknown) | |
| 244 AudioDeviceStop(device_, io_proc_id_); | |
| 245 | |
| 246 is_playing_ = false; | |
| 247 } | |
| 248 | |
| 249 void AudioHardwareUnifiedStream::SetVolume(double volume) { | |
| 250 volume_ = static_cast<float>(volume); | |
| 251 // TODO(crogers): set volume property | |
| 252 } | |
| 253 | |
| 254 void AudioHardwareUnifiedStream::GetVolume(double* volume) { | |
| 255 *volume = volume_; | |
| 256 } | |
| 257 | |
| 258 // Pulls on our provider with optional input, asking it to render output. | |
| 259 // Note to future hackers of this function: Do not add locks here because this | |
| 260 // is running on a real-time thread (for low-latency). | |
| 261 OSStatus AudioHardwareUnifiedStream::Render( | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
Should we have two Render() methods for interleave
Chris Rogers
2012/09/10 22:19:06
The problem is that the interleave vs. non-interle
| |
| 262 AudioDeviceID inDevice, | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
unix_hacker
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 263 const AudioTimeStamp* inNow, | |
| 264 const AudioBufferList* inInputData, | |
| 265 const AudioTimeStamp* inInputTime, | |
| 266 AudioBufferList* outOutputData, | |
| 267 const AudioTimeStamp* inOutputTime) { | |
| 268 // Convert the input data accounting for possible interleaving. | |
| 269 // TODO(crogers): its better to simply memcpy() if source is already planar. | |
|
no longer working on chromium
2012/09/10 12:14:59
nit, it's or remove its
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 270 if (input_channels_ >= client_input_channels_) { | |
| 271 for (unsigned channel_index = 0; channel_index < client_input_channels_; | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
s/unsigned/int/
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 272 ++channel_index) { | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
indent one more space
no longer working on chromium
2012/09/10 12:14:59
nit, fix the indentation.
Chris Rogers
2012/09/10 22:19:06
Done.
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 273 float* sourceP; | |
|
no longer working on chromium
2012/09/10 12:14:59
source, or source_ptr?
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 274 | |
| 275 unsigned source_channel_index = channel_index; | |
| 276 | |
| 277 if (input_channels_per_frame_ > 1) { | |
| 278 // Interleaved. | |
| 279 sourceP = static_cast<float*>(inInputData->mBuffers[0].mData) + | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
unix_hacker
perhaps _ptr instead of P?
Chris Rogers
2012/09/10 22:19:06
just simplified to |source|
| |
| 280 source_channel_index; | |
| 281 } else { | |
| 282 // Non-interleaved. | |
| 283 sourceP = static_cast<float*>( | |
| 284 inInputData->mBuffers[source_channel_index].mData); | |
| 285 } | |
| 286 | |
| 287 float* p = input_bus_->channel(channel_index); | |
| 288 for (unsigned i = 0; i < number_of_frames_; ++i) { | |
| 289 p[i] = *sourceP; | |
| 290 sourceP += input_channels_per_frame_; | |
| 291 } | |
| 292 } | |
| 293 } else if (input_channels_) { | |
| 294 input_bus_->Zero(); | |
| 295 } | |
| 296 | |
| 297 // Give the client optional input data and have it render the output data. | |
| 298 source_->OnMoreIOData(input_bus_.get(), | |
| 299 output_bus_.get(), | |
| 300 AudioBuffersState(0, 0)); | |
| 301 | |
| 302 // TODO(crogers): handle final Core Audio 5.1 layout for 5.1 audio. | |
| 303 | |
| 304 // Handle interleaving as necessary. | |
| 305 // TODO(crogers): its better to simply memcpy() if dest is already planar. | |
| 306 | |
| 307 for (unsigned channel_index = 0; channel_index < format_.mChannelsPerFrame; | |
| 308 ++channel_index) { | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
indent one more space
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 309 float* destP; | |
|
no longer working on chromium
2012/09/10 12:14:59
dest or dest_ptr
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 310 | |
| 311 unsigned dest_channel_index = channel_index + DEST_CHANNEL_OFFSET; | |
| 312 | |
| 313 if (output_channels_per_frame_ > 1) { | |
| 314 // Interleaved. | |
| 315 destP = static_cast<float*>(outOutputData->mBuffers[0].mData) + | |
| 316 dest_channel_index; | |
| 317 } else { | |
| 318 // Non-interleaved. | |
| 319 destP = static_cast<float*>( | |
| 320 outOutputData->mBuffers[dest_channel_index].mData); | |
| 321 } | |
| 322 | |
| 323 float* p = output_bus_->channel(channel_index); | |
| 324 for (unsigned i = 0; i < number_of_frames_; ++i) { | |
| 325 *destP = p[i]; | |
| 326 destP += output_channels_per_frame_; | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 return noErr; | |
| 331 } | |
| 332 | |
| 333 OSStatus AudioHardwareUnifiedStream::RenderProc( | |
| 334 AudioDeviceID inDevice, | |
|
scherkus (not reviewing)
2012/09/10 11:26:19
unix_hacker
Chris Rogers
2012/09/10 22:19:06
Done.
| |
| 335 const AudioTimeStamp* inNow, | |
| 336 const AudioBufferList* inInputData, | |
| 337 const AudioTimeStamp* inInputTime, | |
| 338 AudioBufferList* outOutputData, | |
| 339 const AudioTimeStamp* inOutputTime, | |
| 340 void* user_data) { | |
| 341 AudioHardwareUnifiedStream* audio_output = | |
| 342 static_cast<AudioHardwareUnifiedStream*>(user_data); | |
| 343 DCHECK(audio_output); | |
| 344 if (!audio_output) | |
| 345 return -1; | |
| 346 | |
| 347 return audio_output->Render( | |
| 348 inDevice, | |
| 349 inNow, | |
| 350 inInputData, | |
| 351 inInputTime, | |
| 352 outOutputData, | |
| 353 inOutputTime); | |
| 354 } | |
| 355 | |
| 356 } // namespace media | |
| OLD | NEW |