media/libcubeb/src/cubeb_audiounit.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /*
     2  * Copyright © 2011 Mozilla Foundation
     3  *
     4  * This program is made available under an ISC-style license.  See the
     5  * accompanying file LICENSE for details.
     6  */
     7 #undef NDEBUG
     8 #include <assert.h>
     9 #include <pthread.h>
    10 #include <stdlib.h>
    11 #include <AudioUnit/AudioUnit.h>
    12 #include <CoreAudio/AudioHardware.h>
    13 #include <CoreAudio/HostTime.h>
    14 #include <CoreFoundation/CoreFoundation.h>
    15 #include "cubeb/cubeb.h"
    16 #include "cubeb-internal.h"
    18 #if !defined(kCFCoreFoundationVersionNumber10_7)
    19 /* From CoreFoundation CFBase.h */
    20 #define kCFCoreFoundationVersionNumber10_7 635.00
    21 #endif
    23 #define CUBEB_STREAM_MAX 16
    24 #define NBUFS 4
    26 static struct cubeb_ops const audiounit_ops;
    28 struct cubeb {
    29   struct cubeb_ops const * ops;
    30   pthread_mutex_t mutex;
    31   int active_streams;
    32   int limit_streams;
    33 };
    35 struct cubeb_stream {
    36   cubeb * context;
    37   AudioUnit unit;
    38   cubeb_data_callback data_callback;
    39   cubeb_state_callback state_callback;
    40   void * user_ptr;
    41   AudioStreamBasicDescription sample_spec;
    42   pthread_mutex_t mutex;
    43   uint64_t frames_played;
    44   uint64_t frames_queued;
    45   int shutdown;
    46   int draining;
    47   uint64_t current_latency_frames;
    48   uint64_t hw_latency_frames;
    49 };
    51 static int64_t
    52 audiotimestamp_to_latency(AudioTimeStamp const * tstamp, cubeb_stream * stream)
    53 {
    54   if (!(tstamp->mFlags & kAudioTimeStampHostTimeValid)) {
    55     return 0;
    56   }
    58   uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime);
    59   uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
    61   return ((pres - now) * stream->sample_spec.mSampleRate) / 1000000000LL;
    62 }
    64 static OSStatus
    65 audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
    66                           AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
    67                           AudioBufferList * bufs)
    68 {
    69   cubeb_stream * stm;
    70   unsigned char * buf;
    71   long got;
    72   OSStatus r;
    74   assert(bufs->mNumberBuffers == 1);
    75   buf = bufs->mBuffers[0].mData;
    77   stm = user_ptr;
    79   pthread_mutex_lock(&stm->mutex);
    81   stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
    83   if (stm->draining || stm->shutdown) {
    84     pthread_mutex_unlock(&stm->mutex);
    85     if (stm->draining) {
    86       r = AudioOutputUnitStop(stm->unit);
    87       assert(r == 0);
    88       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
    89     }
    90     return noErr;
    91   }
    93   pthread_mutex_unlock(&stm->mutex);
    94   got = stm->data_callback(stm, stm->user_ptr, buf, nframes);
    95   pthread_mutex_lock(&stm->mutex);
    96   if (got < 0) {
    97     /* XXX handle this case. */
    98     assert(false);
    99     pthread_mutex_unlock(&stm->mutex);
   100     return noErr;
   101   }
   103   if ((UInt32) got < nframes) {
   104     size_t got_bytes = got * stm->sample_spec.mBytesPerFrame;
   105     size_t rem_bytes = (nframes - got) * stm->sample_spec.mBytesPerFrame;
   107     stm->draining = 1;
   109     memset(buf + got_bytes, 0, rem_bytes);
   110   }
   112   stm->frames_played = stm->frames_queued;
   113   stm->frames_queued += got;
   114   pthread_mutex_unlock(&stm->mutex);
   116   return noErr;
   117 }
   119 /*static*/ int
   120 audiounit_init(cubeb ** context, char const * context_name)
   121 {
   122   cubeb * ctx;
   123   int r;
   125   *context = NULL;
   127   ctx = calloc(1, sizeof(*ctx));
   128   assert(ctx);
   130   ctx->ops = &audiounit_ops;
   132   r = pthread_mutex_init(&ctx->mutex, NULL);
   133   assert(r == 0);
   135   ctx->active_streams = 0;
   137   ctx->limit_streams = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7;
   139   *context = ctx;
   141   return CUBEB_OK;
   142 }
   144 static char const *
   145 audiounit_get_backend_id(cubeb * ctx)
   146 {
   147   return "audiounit";
   148 }
   150 static int
   151 audiounit_get_output_device_id(AudioDeviceID * device_id)
   152 {
   153   UInt32 size;
   154   OSStatus r;
   155   AudioObjectPropertyAddress output_device_address = {
   156     kAudioHardwarePropertyDefaultOutputDevice,
   157     kAudioObjectPropertyScopeGlobal,
   158     kAudioObjectPropertyElementMaster
   159   };
   161   size = sizeof(*device_id);
   163   r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
   164                                  &output_device_address,
   165                                  0,
   166                                  NULL,
   167                                  &size,
   168                                  device_id);
   169   if (r != noErr) {
   170     return CUBEB_ERROR;
   171   }
   173   return CUBEB_OK;
   174 }
   176 /* Get the acceptable buffer size (in frames) that this device can work with. */
   177 static int
   178 audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
   179 {
   180   UInt32 size;
   181   OSStatus r;
   182   AudioDeviceID output_device_id;
   183   AudioObjectPropertyAddress output_device_buffer_size_range = {
   184     kAudioDevicePropertyBufferFrameSizeRange,
   185     kAudioDevicePropertyScopeOutput,
   186     kAudioObjectPropertyElementMaster
   187   };
   189   if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
   190     return CUBEB_ERROR;
   191   }
   193   /* Get the buffer size range this device supports */
   194   size = sizeof(*latency_range);
   196   r = AudioObjectGetPropertyData(output_device_id,
   197                                  &output_device_buffer_size_range,
   198                                  0,
   199                                  NULL,
   200                                  &size,
   201                                  latency_range);
   202   if (r != noErr) {
   203     return CUBEB_ERROR;
   204   }
   206   return CUBEB_OK;
   207 }
   209 int
   210 audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
   211 {
   212   UInt32 size;
   213   OSStatus r;
   214   AudioDeviceID output_device_id;
   215   AudioStreamBasicDescription stream_format;
   216   AudioObjectPropertyAddress stream_format_address = {
   217     kAudioDevicePropertyStreamFormat,
   218     kAudioDevicePropertyScopeOutput,
   219     kAudioObjectPropertyElementMaster
   220   };
   222   assert(ctx && max_channels);
   224   if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
   225     return CUBEB_ERROR;
   226   }
   228   size = sizeof(stream_format);
   230   r = AudioObjectGetPropertyData(output_device_id,
   231                                  &stream_format_address,
   232                                  0,
   233                                  NULL,
   234                                  &size,
   235                                  &stream_format);
   236   if (r != noErr) {
   237     return CUBEB_ERROR;
   238   }
   240   *max_channels = stream_format.mChannelsPerFrame;
   242   return CUBEB_OK;
   243 }
   245 static int
   246 audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
   247 {
   248   AudioValueRange latency_range;
   250   if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
   251     return CUBEB_ERROR;
   252   }
   254   *latency_ms = (latency_range.mMinimum * 1000 + params.rate - 1) / params.rate;
   256   return CUBEB_OK;
   257 }
   259 static int
   260 audiounit_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
   261 {
   262   UInt32 size;
   263   OSStatus r;
   264   Float64 fsamplerate;
   265   AudioDeviceID output_device_id;
   266   AudioObjectPropertyAddress samplerate_address = {
   267     kAudioDevicePropertyNominalSampleRate,
   268     kAudioObjectPropertyScopeGlobal,
   269     kAudioObjectPropertyElementMaster
   270   };
   272   if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
   273     return CUBEB_ERROR;
   274   }
   276   size = sizeof(fsamplerate);
   277   r = AudioObjectGetPropertyData(output_device_id,
   278                                  &samplerate_address,
   279                                  0,
   280                                  NULL,
   281                                  &size,
   282                                  &fsamplerate);
   284   if (r != noErr) {
   285     return CUBEB_ERROR;
   286   }
   288   *rate = (uint32_t)fsamplerate;
   290   return CUBEB_OK;
   291 }
   293 static void
   294 audiounit_destroy(cubeb * ctx)
   295 {
   296   int r;
   298   assert(ctx->active_streams == 0);
   300   r = pthread_mutex_destroy(&ctx->mutex);
   301   assert(r == 0);
   303   free(ctx);
   304 }
   306 static void audiounit_stream_destroy(cubeb_stream * stm);
   308 static int
   309 audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
   310                       cubeb_stream_params stream_params, unsigned int latency,
   311                       cubeb_data_callback data_callback, cubeb_state_callback state_callback,
   312                       void * user_ptr)
   313 {
   314   AudioStreamBasicDescription ss;
   315 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   316   ComponentDescription desc;
   317   Component comp;
   318 #else
   319   AudioComponentDescription desc;
   320   AudioComponent comp;
   321 #endif
   322   cubeb_stream * stm;
   323   AURenderCallbackStruct input;
   324   unsigned int buffer_size, default_buffer_size;
   325   OSStatus r;
   326   UInt32 size;
   327   AudioDeviceID output_device_id;
   328   AudioValueRange latency_range;
   330   assert(context);
   331   *stream = NULL;
   333   memset(&ss, 0, sizeof(ss));
   334   ss.mFormatFlags = 0;
   336   switch (stream_params.format) {
   337   case CUBEB_SAMPLE_S16LE:
   338     ss.mBitsPerChannel = 16;
   339     ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
   340     break;
   341   case CUBEB_SAMPLE_S16BE:
   342     ss.mBitsPerChannel = 16;
   343     ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger |
   344                        kAudioFormatFlagIsBigEndian;
   345     break;
   346   case CUBEB_SAMPLE_FLOAT32LE:
   347     ss.mBitsPerChannel = 32;
   348     ss.mFormatFlags |= kAudioFormatFlagIsFloat;
   349     break;
   350   case CUBEB_SAMPLE_FLOAT32BE:
   351     ss.mBitsPerChannel = 32;
   352     ss.mFormatFlags |= kAudioFormatFlagIsFloat |
   353                        kAudioFormatFlagIsBigEndian;
   354     break;
   355   default:
   356     return CUBEB_ERROR_INVALID_FORMAT;
   357   }
   359   ss.mFormatID = kAudioFormatLinearPCM;
   360   ss.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
   361   ss.mSampleRate = stream_params.rate;
   362   ss.mChannelsPerFrame = stream_params.channels;
   364   ss.mBytesPerFrame = (ss.mBitsPerChannel / 8) * ss.mChannelsPerFrame;
   365   ss.mFramesPerPacket = 1;
   366   ss.mBytesPerPacket = ss.mBytesPerFrame * ss.mFramesPerPacket;
   368   pthread_mutex_lock(&context->mutex);
   369   if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
   370     pthread_mutex_unlock(&context->mutex);
   371     return CUBEB_ERROR;
   372   }
   373   context->active_streams += 1;
   374   pthread_mutex_unlock(&context->mutex);
   376   desc.componentType = kAudioUnitType_Output;
   377   desc.componentSubType = kAudioUnitSubType_DefaultOutput;
   378   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
   379   desc.componentFlags = 0;
   380   desc.componentFlagsMask = 0;
   381 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   382   comp = FindNextComponent(NULL, &desc);
   383 #else
   384   comp = AudioComponentFindNext(NULL, &desc);
   385 #endif
   386   assert(comp);
   388   stm = calloc(1, sizeof(*stm));
   389   assert(stm);
   391   stm->context = context;
   392   stm->data_callback = data_callback;
   393   stm->state_callback = state_callback;
   394   stm->user_ptr = user_ptr;
   396   stm->sample_spec = ss;
   398   r = pthread_mutex_init(&stm->mutex, NULL);
   399   assert(r == 0);
   401   stm->frames_played = 0;
   402   stm->frames_queued = 0;
   403   stm->current_latency_frames = 0;
   404   stm->hw_latency_frames = UINT64_MAX;
   406 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   407   r = OpenAComponent(comp, &stm->unit);
   408 #else
   409   r = AudioComponentInstanceNew(comp, &stm->unit);
   410 #endif
   411   if (r != 0) {
   412     audiounit_stream_destroy(stm);
   413     return CUBEB_ERROR;
   414   }
   416   input.inputProc = audiounit_output_callback;
   417   input.inputProcRefCon = stm;
   418   r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_SetRenderCallback,
   419                            kAudioUnitScope_Global, 0, &input, sizeof(input));
   420   if (r != 0) {
   421     audiounit_stream_destroy(stm);
   422     return CUBEB_ERROR;
   423   }
   425   buffer_size = latency / 1000.0 * ss.mSampleRate;
   427   /* Get the range of latency this particular device can work with, and clamp
   428    * the requested latency to this acceptable range. */
   429   if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
   430     audiounit_stream_destroy(stm);
   431     return CUBEB_ERROR;
   432   }
   434   if (buffer_size < (unsigned int) latency_range.mMinimum) {
   435     buffer_size = (unsigned int) latency_range.mMinimum;
   436   } else if (buffer_size > (unsigned int) latency_range.mMaximum) {
   437     buffer_size = (unsigned int) latency_range.mMaximum;
   438   }
   440   /**
   441    * Get the default buffer size. If our latency request is below the default,
   442    * set it. Otherwise, use the default latency.
   443    **/
   444   size = sizeof(default_buffer_size);
   445   r = AudioUnitGetProperty(stm->unit, kAudioDevicePropertyBufferFrameSize,
   446                            kAudioUnitScope_Output, 0, &default_buffer_size, &size);
   448   if (r != 0) {
   449     audiounit_stream_destroy(stm);
   450     return CUBEB_ERROR;
   451   }
   453   // Setting the latency doesn't work well for USB headsets (eg. plantronics).
   454   // Keep the default latency for now.
   455 #if 0
   456   if (buffer_size < default_buffer_size) {
   457     /* Set the maximum number of frame that the render callback will ask for,
   458      * effectively setting the latency of the stream. This is process-wide. */
   459     r = AudioUnitSetProperty(stm->unit, kAudioDevicePropertyBufferFrameSize,
   460                              kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size));
   461     if (r != 0) {
   462       audiounit_stream_destroy(stm);
   463       return CUBEB_ERROR;
   464     }
   465   }
   466 #endif
   468   r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
   469                            0, &ss, sizeof(ss));
   470   if (r != 0) {
   471     audiounit_stream_destroy(stm);
   472     return CUBEB_ERROR;
   473   }
   475   r = AudioUnitInitialize(stm->unit);
   476   if (r != 0) {
   477     audiounit_stream_destroy(stm);
   478     return CUBEB_ERROR;
   479   }
   481   *stream = stm;
   483   return CUBEB_OK;
   484 }
   486 static void
   487 audiounit_stream_destroy(cubeb_stream * stm)
   488 {
   489   int r;
   491   stm->shutdown = 1;
   493   if (stm->unit) {
   494     AudioOutputUnitStop(stm->unit);
   495     AudioUnitUninitialize(stm->unit);
   496 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   497     CloseComponent(stm->unit);
   498 #else
   499     AudioComponentInstanceDispose(stm->unit);
   500 #endif
   501   }
   503   r = pthread_mutex_destroy(&stm->mutex);
   504   assert(r == 0);
   506   pthread_mutex_lock(&stm->context->mutex);
   507   assert(stm->context->active_streams >= 1);
   508   stm->context->active_streams -= 1;
   509   pthread_mutex_unlock(&stm->context->mutex);
   511   free(stm);
   512 }
   514 static int
   515 audiounit_stream_start(cubeb_stream * stm)
   516 {
   517   OSStatus r;
   518   r = AudioOutputUnitStart(stm->unit);
   519   assert(r == 0);
   520   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
   521   return CUBEB_OK;
   522 }
   524 static int
   525 audiounit_stream_stop(cubeb_stream * stm)
   526 {
   527   OSStatus r;
   528   r = AudioOutputUnitStop(stm->unit);
   529   assert(r == 0);
   530   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   531   return CUBEB_OK;
   532 }
   534 static int
   535 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
   536 {
   537   pthread_mutex_lock(&stm->mutex);
   538   *position = stm->frames_played;
   539   pthread_mutex_unlock(&stm->mutex);
   540   return CUBEB_OK;
   541 }
   543 int
   544 audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
   545 {
   546   pthread_mutex_lock(&stm->mutex);
   547   if (stm->hw_latency_frames == UINT64_MAX) {
   548     UInt32 size;
   549     uint32_t device_latency_frames, device_safety_offset;
   550     double unit_latency_sec;
   551     AudioDeviceID output_device_id;
   552     OSStatus r;
   553     AudioObjectPropertyAddress latency_address = {
   554       kAudioDevicePropertyLatency,
   555       kAudioDevicePropertyScopeOutput,
   556       kAudioObjectPropertyElementMaster
   557     };
   558     AudioObjectPropertyAddress safety_offset_address = {
   559       kAudioDevicePropertySafetyOffset,
   560       kAudioDevicePropertyScopeOutput,
   561       kAudioObjectPropertyElementMaster
   562     };
   564     r = audiounit_get_output_device_id(&output_device_id);
   566     if (r != noErr) {
   567       pthread_mutex_unlock(&stm->mutex);
   568       return CUBEB_ERROR;
   569     }
   571     size = sizeof(unit_latency_sec);
   572     r = AudioUnitGetProperty(stm->unit,
   573                              kAudioUnitProperty_Latency,
   574                              kAudioUnitScope_Global,
   575                              0,
   576                              &unit_latency_sec,
   577                              &size);
   578     if (r != noErr) {
   579       pthread_mutex_unlock(&stm->mutex);
   580       return CUBEB_ERROR;
   581     }
   583     size = sizeof(device_latency_frames);
   584     r = AudioObjectGetPropertyData(output_device_id,
   585                                    &latency_address,
   586                                    0,
   587                                    NULL,
   588                                    &size,
   589                                    &device_latency_frames);
   590     if (r != noErr) {
   591       pthread_mutex_unlock(&stm->mutex);
   592       return CUBEB_ERROR;
   593     }
   595     size = sizeof(device_safety_offset);
   596     r = AudioObjectGetPropertyData(output_device_id,
   597                                    &safety_offset_address,
   598                                    0,
   599                                    NULL,
   600                                    &size,
   601                                    &device_safety_offset);
   602     if (r != noErr) {
   603       pthread_mutex_unlock(&stm->mutex);
   604       return CUBEB_ERROR;
   605     }
   607     /* This part is fixed and depend on the stream parameter and the hardware. */
   608     stm->hw_latency_frames =
   609       (uint32_t)(unit_latency_sec * stm->sample_spec.mSampleRate)
   610       + device_latency_frames
   611       + device_safety_offset;
   612   }
   614   *latency = stm->hw_latency_frames + stm->current_latency_frames;
   615   pthread_mutex_unlock(&stm->mutex);
   617   return CUBEB_OK;
   618 }
   620 static struct cubeb_ops const audiounit_ops = {
   621   .init = audiounit_init,
   622   .get_backend_id = audiounit_get_backend_id,
   623   .get_max_channel_count = audiounit_get_max_channel_count,
   624   .get_min_latency = audiounit_get_min_latency,
   625   .get_preferred_sample_rate = audiounit_get_preferred_sample_rate,
   626   .destroy = audiounit_destroy,
   627   .stream_init = audiounit_stream_init,
   628   .stream_destroy = audiounit_stream_destroy,
   629   .stream_start = audiounit_stream_start,
   630   .stream_stop = audiounit_stream_stop,
   631   .stream_get_position = audiounit_stream_get_position,
   632   .stream_get_latency = audiounit_stream_get_latency
   633 };

mercurial