• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/mac/audio_manager_mac.h"
13 
14 namespace media {
15 
16 // TODO(crogers): support more than hard-coded stereo input.
17 // Ideally we would like to receive this value as a constructor argument.
18 static const int kDefaultInputChannels = 2;
19 
AudioHardwareUnifiedStream(AudioManagerMac * manager,const AudioParameters & params)20 AudioHardwareUnifiedStream::AudioHardwareUnifiedStream(
21     AudioManagerMac* manager, const AudioParameters& params)
22     : manager_(manager),
23       source_(NULL),
24       client_input_channels_(kDefaultInputChannels),
25       volume_(1.0f),
26       input_channels_(0),
27       output_channels_(0),
28       input_channels_per_frame_(0),
29       output_channels_per_frame_(0),
30       io_proc_id_(0),
31       device_(kAudioObjectUnknown),
32       is_playing_(false) {
33   DCHECK(manager_);
34 
35   // A frame is one sample across all channels. In interleaved audio the per
36   // frame fields identify the set of n |channels|. In uncompressed audio, a
37   // packet is always one frame.
38   format_.mSampleRate = params.sample_rate();
39   format_.mFormatID = kAudioFormatLinearPCM;
40   format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
41                          kLinearPCMFormatFlagIsSignedInteger;
42   format_.mBitsPerChannel = params.bits_per_sample();
43   format_.mChannelsPerFrame = params.channels();
44   format_.mFramesPerPacket = 1;
45   format_.mBytesPerPacket = (format_.mBitsPerChannel * params.channels()) / 8;
46   format_.mBytesPerFrame = format_.mBytesPerPacket;
47   format_.mReserved = 0;
48 
49   // Calculate the number of sample frames per callback.
50   number_of_frames_ = params.GetBytesPerBuffer() / format_.mBytesPerPacket;
51 
52   input_bus_ = AudioBus::Create(client_input_channels_,
53                                 params.frames_per_buffer());
54   output_bus_ = AudioBus::Create(params);
55 }
56 
~AudioHardwareUnifiedStream()57 AudioHardwareUnifiedStream::~AudioHardwareUnifiedStream() {
58   DCHECK_EQ(device_, kAudioObjectUnknown);
59 }
60 
Open()61 bool AudioHardwareUnifiedStream::Open() {
62   // Obtain the current output device selected by the user.
63   AudioObjectPropertyAddress pa;
64   pa.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
65   pa.mScope = kAudioObjectPropertyScopeGlobal;
66   pa.mElement = kAudioObjectPropertyElementMaster;
67 
68   UInt32 size = sizeof(device_);
69 
70   OSStatus result = AudioObjectGetPropertyData(
71       kAudioObjectSystemObject,
72       &pa,
73       0,
74       0,
75       &size,
76       &device_);
77 
78   if ((result != kAudioHardwareNoError) || (device_ == kAudioDeviceUnknown)) {
79     LOG(ERROR) << "Cannot open unified AudioDevice.";
80     return false;
81   }
82 
83   // The requested sample-rate must match the hardware sample-rate.
84   Float64 sample_rate = 0.0;
85   size = sizeof(sample_rate);
86 
87   pa.mSelector = kAudioDevicePropertyNominalSampleRate;
88   pa.mScope = kAudioObjectPropertyScopeWildcard;
89   pa.mElement = kAudioObjectPropertyElementMaster;
90 
91   result = AudioObjectGetPropertyData(
92       device_,
93       &pa,
94       0,
95       0,
96       &size,
97       &sample_rate);
98 
99   if (result != noErr || sample_rate != format_.mSampleRate) {
100     LOG(ERROR) << "Requested sample-rate: " << format_.mSampleRate
101         <<  " must match the hardware sample-rate: " << sample_rate;
102     return false;
103   }
104 
105   // Configure buffer frame size.
106   UInt32 frame_size = number_of_frames_;
107 
108   pa.mSelector = kAudioDevicePropertyBufferFrameSize;
109   pa.mScope = kAudioDevicePropertyScopeInput;
110   pa.mElement = kAudioObjectPropertyElementMaster;
111   result = AudioObjectSetPropertyData(
112       device_,
113       &pa,
114       0,
115       0,
116       sizeof(frame_size),
117       &frame_size);
118 
119   if (result != noErr) {
120     LOG(ERROR) << "Unable to set input buffer frame size: "  << frame_size;
121     return false;
122   }
123 
124   pa.mScope = kAudioDevicePropertyScopeOutput;
125   result = AudioObjectSetPropertyData(
126       device_,
127       &pa,
128       0,
129       0,
130       sizeof(frame_size),
131       &frame_size);
132 
133   if (result != noErr) {
134     LOG(ERROR) << "Unable to set output buffer frame size: "  << frame_size;
135     return false;
136   }
137 
138   DVLOG(1) << "Sample rate: " << sample_rate;
139   DVLOG(1) << "Frame size: " << frame_size;
140 
141   // Determine the number of input and output channels.
142   // We handle both the interleaved and non-interleaved cases.
143 
144   // Get input stream configuration.
145   pa.mSelector = kAudioDevicePropertyStreamConfiguration;
146   pa.mScope = kAudioDevicePropertyScopeInput;
147   pa.mElement = kAudioObjectPropertyElementMaster;
148 
149   result = AudioObjectGetPropertyDataSize(device_, &pa, 0, 0, &size);
150   OSSTATUS_DCHECK(result == noErr, result);
151 
152   if (result == noErr && size > 0) {
153     // Allocate storage.
154     scoped_ptr<uint8[]> input_list_storage(new uint8[size]);
155     AudioBufferList& input_list =
156         *reinterpret_cast<AudioBufferList*>(input_list_storage.get());
157 
158     result = AudioObjectGetPropertyData(
159         device_,
160         &pa,
161         0,
162         0,
163         &size,
164         &input_list);
165     OSSTATUS_DCHECK(result == noErr, result);
166 
167     if (result == noErr) {
168       // Determine number of input channels.
169       input_channels_per_frame_ = input_list.mNumberBuffers > 0 ?
170           input_list.mBuffers[0].mNumberChannels : 0;
171       if (input_channels_per_frame_ == 1 && input_list.mNumberBuffers > 1) {
172         // Non-interleaved.
173         input_channels_ = input_list.mNumberBuffers;
174       } else {
175         // Interleaved.
176         input_channels_ = input_channels_per_frame_;
177       }
178     }
179   }
180 
181   DVLOG(1) << "Input channels: " << input_channels_;
182   DVLOG(1) << "Input channels per frame: " << input_channels_per_frame_;
183 
184   // The hardware must have at least the requested input channels.
185   if (result != noErr || client_input_channels_ > input_channels_) {
186     LOG(ERROR) << "AudioDevice does not support requested input channels.";
187     return false;
188   }
189 
190   // Get output stream configuration.
191   pa.mSelector = kAudioDevicePropertyStreamConfiguration;
192   pa.mScope = kAudioDevicePropertyScopeOutput;
193   pa.mElement = kAudioObjectPropertyElementMaster;
194 
195   result = AudioObjectGetPropertyDataSize(device_, &pa, 0, 0, &size);
196   OSSTATUS_DCHECK(result == noErr, result);
197 
198   if (result == noErr && size > 0) {
199     // Allocate storage.
200     scoped_ptr<uint8[]> output_list_storage(new uint8[size]);
201     AudioBufferList& output_list =
202         *reinterpret_cast<AudioBufferList*>(output_list_storage.get());
203 
204     result = AudioObjectGetPropertyData(
205         device_,
206         &pa,
207         0,
208         0,
209         &size,
210         &output_list);
211     OSSTATUS_DCHECK(result == noErr, result);
212 
213     if (result == noErr) {
214       // Determine number of output channels.
215       output_channels_per_frame_ = output_list.mBuffers[0].mNumberChannels;
216       if (output_channels_per_frame_ == 1 && output_list.mNumberBuffers > 1) {
217         // Non-interleaved.
218         output_channels_ = output_list.mNumberBuffers;
219       } else {
220         // Interleaved.
221         output_channels_ = output_channels_per_frame_;
222       }
223     }
224   }
225 
226   DVLOG(1) << "Output channels: " << output_channels_;
227   DVLOG(1) << "Output channels per frame: " << output_channels_per_frame_;
228 
229   // The hardware must have at least the requested output channels.
230   if (result != noErr ||
231       output_channels_ < static_cast<int>(format_.mChannelsPerFrame)) {
232     LOG(ERROR) << "AudioDevice does not support requested output channels.";
233     return false;
234   }
235 
236   // Setup the I/O proc.
237   result = AudioDeviceCreateIOProcID(device_, RenderProc, this, &io_proc_id_);
238   if (result != noErr) {
239     LOG(ERROR) << "Error creating IOProc.";
240     return false;
241   }
242 
243   return true;
244 }
245 
Close()246 void AudioHardwareUnifiedStream::Close() {
247   DCHECK(!is_playing_);
248 
249   OSStatus result = AudioDeviceDestroyIOProcID(device_, io_proc_id_);
250   OSSTATUS_DCHECK(result == noErr, result);
251 
252   io_proc_id_ = 0;
253   device_ = kAudioObjectUnknown;
254 
255   // Inform the audio manager that we have been closed. This can cause our
256   // destruction.
257   manager_->ReleaseOutputStream(this);
258 }
259 
Start(AudioSourceCallback * callback)260 void AudioHardwareUnifiedStream::Start(AudioSourceCallback* callback) {
261   DCHECK(callback);
262   DCHECK_NE(device_, kAudioObjectUnknown);
263   DCHECK(!is_playing_);
264   if (device_ == kAudioObjectUnknown || is_playing_)
265     return;
266 
267   source_ = callback;
268 
269   OSStatus result = AudioDeviceStart(device_, io_proc_id_);
270   OSSTATUS_DCHECK(result == noErr, result);
271 
272   if (result == noErr)
273     is_playing_ = true;
274 }
275 
Stop()276 void AudioHardwareUnifiedStream::Stop() {
277   if (!is_playing_)
278     return;
279 
280   if (device_ != kAudioObjectUnknown) {
281     OSStatus result = AudioDeviceStop(device_, io_proc_id_);
282     OSSTATUS_DCHECK(result == noErr, result);
283   }
284 
285   is_playing_ = false;
286   source_ = NULL;
287 }
288 
SetVolume(double volume)289 void AudioHardwareUnifiedStream::SetVolume(double volume) {
290   volume_ = static_cast<float>(volume);
291   // TODO(crogers): set volume property
292 }
293 
GetVolume(double * volume)294 void AudioHardwareUnifiedStream::GetVolume(double* volume) {
295   *volume = volume_;
296 }
297 
298 // Pulls on our provider with optional input, asking it to render output.
299 // Note to future hackers of this function: Do not add locks here because this
300 // is running on a real-time thread (for low-latency).
Render(AudioDeviceID device,const AudioTimeStamp * now,const AudioBufferList * input_data,const AudioTimeStamp * input_time,AudioBufferList * output_data,const AudioTimeStamp * output_time)301 OSStatus AudioHardwareUnifiedStream::Render(
302     AudioDeviceID device,
303     const AudioTimeStamp* now,
304     const AudioBufferList* input_data,
305     const AudioTimeStamp* input_time,
306     AudioBufferList* output_data,
307     const AudioTimeStamp* output_time) {
308   // Convert the input data accounting for possible interleaving.
309   // TODO(crogers): it's better to simply memcpy() if source is already planar.
310   if (input_channels_ >= client_input_channels_) {
311     for (int channel_index = 0; channel_index < client_input_channels_;
312          ++channel_index) {
313       float* source;
314 
315       int source_channel_index = channel_index;
316 
317       if (input_channels_per_frame_ > 1) {
318         // Interleaved.
319         source = static_cast<float*>(input_data->mBuffers[0].mData) +
320             source_channel_index;
321       } else {
322         // Non-interleaved.
323         source = static_cast<float*>(
324             input_data->mBuffers[source_channel_index].mData);
325       }
326 
327       float* p = input_bus_->channel(channel_index);
328       for (int i = 0; i < number_of_frames_; ++i) {
329         p[i] = *source;
330         source += input_channels_per_frame_;
331       }
332     }
333   } else if (input_channels_) {
334     input_bus_->Zero();
335   }
336 
337   // Give the client optional input data and have it render the output data.
338   source_->OnMoreIOData(input_bus_.get(),
339                         output_bus_.get(),
340                         AudioBuffersState(0, 0));
341 
342   // TODO(crogers): handle final Core Audio 5.1 layout for 5.1 audio.
343 
344   // Handle interleaving as necessary.
345   // TODO(crogers): it's better to simply memcpy() if dest is already planar.
346 
347   for (int channel_index = 0;
348        channel_index < static_cast<int>(format_.mChannelsPerFrame);
349        ++channel_index) {
350     float* dest;
351 
352     int dest_channel_index = channel_index;
353 
354     if (output_channels_per_frame_ > 1) {
355       // Interleaved.
356       dest = static_cast<float*>(output_data->mBuffers[0].mData) +
357           dest_channel_index;
358     } else {
359       // Non-interleaved.
360       dest = static_cast<float*>(
361           output_data->mBuffers[dest_channel_index].mData);
362     }
363 
364     float* p = output_bus_->channel(channel_index);
365     for (int i = 0; i < number_of_frames_; ++i) {
366       *dest = p[i];
367       dest += output_channels_per_frame_;
368     }
369   }
370 
371   return noErr;
372 }
373 
RenderProc(AudioDeviceID device,const AudioTimeStamp * now,const AudioBufferList * input_data,const AudioTimeStamp * input_time,AudioBufferList * output_data,const AudioTimeStamp * output_time,void * user_data)374 OSStatus AudioHardwareUnifiedStream::RenderProc(
375     AudioDeviceID device,
376     const AudioTimeStamp* now,
377     const AudioBufferList* input_data,
378     const AudioTimeStamp* input_time,
379     AudioBufferList* output_data,
380     const AudioTimeStamp* output_time,
381     void* user_data) {
382   AudioHardwareUnifiedStream* audio_output =
383       static_cast<AudioHardwareUnifiedStream*>(user_data);
384   DCHECK(audio_output);
385   if (!audio_output)
386     return -1;
387 
388   return audio_output->Render(
389       device,
390       now,
391       input_data,
392       input_time,
393       output_data,
394       output_time);
395 }
396 
397 }  // namespace media
398