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 |