Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: media/audio/mac/audio_unified_mac.cc

Issue 10916105: Add Mac OS X unified audio I/O back-end (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698