Home | History | Annotate | Download | only in mac
      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 
     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 
     57 AudioHardwareUnifiedStream::~AudioHardwareUnifiedStream() {
     58   DCHECK_EQ(device_, kAudioObjectUnknown);
     59 }
     60 
     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 
    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 
    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 
    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 
    289 void AudioHardwareUnifiedStream::SetVolume(double volume) {
    290   volume_ = static_cast<float>(volume);
    291   // TODO(crogers): set volume property
    292 }
    293 
    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).
    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 
    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