示例#1
0
/* close the audio device */
int ao_plugin_close(ao_device *device)
{
	ao_alsa_internal *internal;

	if (device) {
          if ((internal = (ao_alsa_internal *) device->internal)) {
            if (internal->pcm_handle) {

              /* this is a PulseAudio ALSA emulation bug workaround;
                 snd_pcm_drain always takes about 2 seconds, even if
                 there's nothing to drain.  Rather than wait for no
                 reason, determine the current playback depth, wait
                 that long, then kill the stream.  Remove this code
                 once Pulse gets fixed. */

              snd_pcm_sframes_t sframes;
              if(snd_pcm_delay (internal->pcm_handle, &sframes)){
                snd_pcm_drain(internal->pcm_handle);
              }else{
                double s = (double)(sframes - internal->static_delay)/internal->sample_rate;
                if(s>1){
                  /* something went wrong; fall back */
                  snd_pcm_drain(internal->pcm_handle);
                }else{
                  if(s>0){
                    struct timespec sleep,wake;
                    sleep.tv_sec = (int)s;
                    sleep.tv_nsec = (s-sleep.tv_sec)*1000000000;
                    while(nanosleep(&sleep,&wake)<0){
                      if(errno==EINTR)
                        sleep=wake;
                      else
                        break;
                    }
                  }
                }
              }
              snd_pcm_close(internal->pcm_handle);
              if(internal->local_config)
                snd_config_delete(internal->local_config);
              internal->local_config=NULL;
              internal->pcm_handle=NULL;
            }
          } else
            awarn("ao_plugin_close called with uninitialized ao_device->internal\n");
	} else
          awarn("ao_plugin_close called with uninitialized ao_device\n");

	return 1;
}
示例#2
0
int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
  {
  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
  int buffer;

  if (!strcmp(key,"buffer_time")) {
    buffer = atoi(value);
    if (buffer < 100) {
      awarn("Buffer time clipped to 100ms\n");
      buffer = 100;
    }
    internal->buffer_time = buffer;
  } else if (!strcmp(key,"dev")) {
    if (!value || !value[0]) {
      /* permit switching back to default device with empty string */
      internal->outputDevice = kAudioObjectUnknown;
    } else {
      internal->outputDevice = findAudioOutputDevice(value);
      if (internal->outputDevice == kAudioObjectUnknown)
        return 0;
    }
  }

  return 1;
}
示例#3
0
/* free the internal data structures */
void ao_plugin_device_clear(ao_device *device)
{
	ao_alsa_internal *internal;

	if (device) {
          if ((internal = (ao_alsa_internal *) device->internal)) {
            if (internal->dev)
              free (internal->dev);
            else
              awarn("ao_plugin_device_clear called with uninitialized ao_device->internal->dev\n");
            if (internal->padbuffer)
              free (internal->padbuffer);
            free(internal);
            device->internal=NULL;
          } else
            awarn("ao_plugin_device_clear called with uninitialized ao_device->internal\n");
	} else
          awarn("ao_plugin_device_clear called with uninitialized ao_device\n");
}
示例#4
0
static int align_value(int src_val)
{
  if (src_val <= 0) {
    awarn("bad align: %d\n", src_val);
    src_val = 1;
  }
  if (!g_arm_mode)
    return src_val;

  return __builtin_ffs(src_val) - 1;
}
示例#5
0
/* prepare the audio device for playback */
int ao_plugin_open(ao_device *device, ao_sample_format *format)
{
	ao_alsa_internal *internal  = (ao_alsa_internal *) device->internal;
	int err,prebits;

	/* Get the ALSA bitformat first to make sure it's valid */
	err = alsa_get_sample_bitformat(format->bits,
                                        device->client_byte_format == AO_FMT_BIG,device);
	if (err < 0){
          aerror("Invalid byte format\n");
          return 0;
        }

	internal->bitformat = err;

        /* Alsa can only use padded 24 bit formatting */
        if(format->bits>16 && format->bits<=24){
          internal->padbuffer = calloc(4096,1);
          internal->padoutw = 32;
        }else{
          internal->padbuffer = 0;
          internal->padoutw = 0;
        }
        prebits = format->bits;

	/* Open the ALSA device */
        err=0;
        if(!internal->dev){
          if(internal->id<0){
            char *tmp=NULL;
            /* we don't try just 'default' as it's a plug device that
               will accept any number of channels but usually plays back
               everything as stereo. */
            switch(device->output_channels){
            default:
            case 8:
            case 7:
              err = alsa_test_open(device, tmp="surround71", format);
              break;
            case 4:
            case 3:
              err = alsa_test_open(device, tmp="surround40", format);
              if(err==0)break;
            case 6:
            case 5:
              err = alsa_test_open(device, tmp="surround51", format);
              break;
            case 2:
              err = alsa_test_open(device, tmp="front", format);
            case 1:
              break;
            }

            if(err){
              awarn("Unable to open surround playback.  Trying default device...\n");
              tmp=NULL;
            }

            if(!tmp)
              err = alsa_test_open(device, tmp="default", format);

            internal->dev=strdup(tmp);
          }else{
            char tmp[80];
            sprintf(tmp,"hw:%d",internal->id);
            internal->dev=strdup(tmp);
            err = alsa_test_open(device, internal->dev, format);
          }
        }else
          err = alsa_test_open(device, internal->dev, format);

	if (err < 0) {
          aerror("Unable to open ALSA device '%s' for playback => %s\n",
                 internal->dev, snd_strerror(err));
          return 0;
	}

        if(prebits != format->bits){
          internal->padbuffer = calloc(4096,1);
          internal->padoutw = (format->bits+7)/8;
          format->bits=prebits;
        }

        adebug("Using ALSA device '%s'\n",internal->dev);
        {
          snd_pcm_sframes_t sframes;
          if(snd_pcm_delay (internal->pcm_handle, &sframes)){
            internal->static_delay=0;
          }else{
            internal->static_delay=sframes;
          }
        }

	/* save the sample size in bytes for posterity */
	internal->sample_size = format->bits * device->output_channels / 8;

	/* alsa's endinness will be the same as the application's */
	if (format->bits > 8)
		device->driver_byte_format = device->client_byte_format;

        if(strcasecmp(internal->dev,"default")){
          if(strncasecmp(internal->dev,"surround",8)){
            if(device->output_channels>2 && device->verbose>=0){
              awarn("No way to determine hardware %d channel mapping of\n"
                    "ALSA device '%s'.\n",device->output_channels, internal->dev);
              if(device->inter_matrix){
                free(device->inter_matrix);
                device->inter_matrix=NULL;
              }
            }
          }
        }

	return 1;
}
示例#6
0
static inline int alsa_test_open(ao_device *device,
                                 char *dev,
                                 ao_sample_format *format)
{
  ao_alsa_internal *internal  = (ao_alsa_internal *) device->internal;
  snd_pcm_hw_params_t   *params;
  int err;

  adebug("Trying to open ALSA device '%s'\n",dev);

  internal->local_config = init_local_config_with_workaround(device,dev);
  if(internal->local_config)
    err = snd_pcm_open_lconf(&(internal->pcm_handle), dev,
                             SND_PCM_STREAM_PLAYBACK, 0, internal->local_config);
  else
    err = snd_pcm_open(&(internal->pcm_handle), dev,
                       SND_PCM_STREAM_PLAYBACK, 0);

  if(err){
    adebug("Unable to open ALSA device '%s'\n",dev);
    if(internal->local_config)
      snd_config_delete(internal->local_config);
    internal->local_config=NULL;
    return err;
  }

  /* try to set up hw params */
  err = alsa_set_hwparams(device,format);
  if(err<0){
    adebug("Unable to open ALSA device '%s'\n",dev);
    snd_pcm_close(internal->pcm_handle);
    if(internal->local_config)
      snd_config_delete(internal->local_config);
    internal->local_config=NULL;
    internal->pcm_handle = NULL;
    return err;
  }

  /* try to set up sw params */
  err = alsa_set_swparams(device);
  if(err<0){
    adebug("Unable to open ALSA device '%s'\n",dev);
    snd_pcm_close(internal->pcm_handle);
    if(internal->local_config)
      snd_config_delete(internal->local_config);
    internal->local_config=NULL;
    internal->pcm_handle = NULL;
    return err;
  }

  /* this is a hack and fragile if the exact device detection code
     flow changes!  Nevertheless, this is a useful warning for users.
     Never fail silently if we can help it! */
  if(!strcasecmp(dev,"default")){
    /* default device */
    if(device->output_channels>2){
      awarn("ALSA 'default' device plays only channels 0,1.\n");
    }
  }
  if(!strcasecmp(dev,"default") || !strncasecmp(dev,"plug",4)){
    if(format->bits>16){
      awarn("ALSA '%s' device may only simulate >16 bit playback\n",dev);
    }
  }

  /* success! */
  return 0;
}
示例#7
0
/* setup alsa data format and buffer geometry */
static inline int alsa_set_hwparams(ao_device *device,
                                    ao_sample_format *format)
{
	ao_alsa_internal *internal  = (ao_alsa_internal *) device->internal;
	snd_pcm_hw_params_t   *params;
	int err;
	unsigned int rate = internal->sample_rate = format->rate;

	/* allocate the hardware parameter structure */
	snd_pcm_hw_params_alloca(&params);

	/* fetch all possible hardware parameters */
	err = snd_pcm_hw_params_any(internal->pcm_handle, params);
	if (err < 0){
          adebug("snd_pcm_hw_params_any() failed.\n"
                 "        Device exists but no matching hardware?\n");
          return err;
        }

	/* set the access type */
	err = snd_pcm_hw_params_set_access(internal->pcm_handle,
			params, internal->access_mask);
	if (err < 0){
          adebug("snd_pcm_hw_params_set_access() failed.\n");
          return err;
        }

	/* set the sample bitformat */
	err = snd_pcm_hw_params_set_format(internal->pcm_handle,
			params, internal->bitformat);
	if (err < 0){

          /* the device may support a greater bit-depth than the one
             requested. */
          switch(internal->bitformat){
          case SND_PCM_FORMAT_U8:
            if (!snd_pcm_hw_params_set_format(internal->pcm_handle,
                                              params, SND_PCM_FORMAT_S16)){
              adebug("snd_pcm_hw_params_set_format() unable to open %d bit playback.\n",format->bits);
              adebug("snd_pcm_hw_params_set_format() using 16 bit playback instead.\n");
              format->bits = 16;
              break;
            }
          case SND_PCM_FORMAT_S16:
            if (!snd_pcm_hw_params_set_format(internal->pcm_handle,
                                              params, SND_PCM_FORMAT_S24)){
              adebug("snd_pcm_hw_params_set_format() unable to open %d bit playback.\n",format->bits);
              adebug("snd_pcm_hw_params_set_format() using 24 bit playback instead.\n");
              format->bits = 24;
              break;
            }
          case SND_PCM_FORMAT_S24:
            if (!snd_pcm_hw_params_set_format(internal->pcm_handle,
                                              params, SND_PCM_FORMAT_S32)){
              adebug("snd_pcm_hw_params_set_format() unable to open %d bit playback.\n",format->bits);
              adebug("snd_pcm_hw_params_set_format() using 32 bit playback instead.\n");
              format->bits = 32;
              break;
            }
          case SND_PCM_FORMAT_S32:
            adebug("snd_pcm_hw_params_set_format() failed.\n");
            return err;
          }
        }

	/* set the number of channels */
	err = snd_pcm_hw_params_set_channels(internal->pcm_handle,
			params, (unsigned int)device->output_channels);
	if (err < 0){
          adebug("snd_pcm_hw_params_set_channels() failed.\n");
          return err;
        }

	/* set the sample rate */
	err = snd_pcm_hw_params_set_rate_near(internal->pcm_handle,
			params, &rate, 0);
	if (err < 0){
          adebug("snd_pcm_hw_params_set_rate_near() failed.\n");
          return err;
        }
	if (rate > 1.05 * format->rate || rate < 0.95 * format->rate) {
          awarn("sample rate %i not supported "
                "by the hardware, using %u\n", format->rate, rate);
	}

	/* set the time per hardware sample transfer */
        if(internal->period_time==0)
          internal->period_time=internal->buffer_time/4;

	err = snd_pcm_hw_params_set_period_time_near(internal->pcm_handle,
			params, &(internal->period_time), 0);
	if (err < 0){
          adebug("snd_pcm_hw_params_set_period_time_near() failed.\n");
          return err;
        }

	/* set the length of the hardware sample buffer in microseconds */
        /* some plug devices have very high minimum periods; don't
           allow a buffer size small enough that it's ~ guaranteed to
           skip */
        if(internal->buffer_time<internal->period_time*3)
          internal->buffer_time=internal->period_time*3;
	err = snd_pcm_hw_params_set_buffer_time_near(internal->pcm_handle,
			params, &(internal->buffer_time), 0);
	if (err < 0){
          adebug("snd_pcm_hw_params_set_buffer_time_near() failed.\n");
          return err;
        }

	/* commit the params structure to the hardware via ALSA */
	err = snd_pcm_hw_params(internal->pcm_handle, params);
	if (err < 0){
          adebug("snd_pcm_hw_params() failed.\n");
          return err;
        }

	/* save the period size in frames for posterity */
	err = snd_pcm_hw_params_get_period_size(params,
						&(internal->period_size), 0);
	if (err < 0){
          adebug("snd_pcm_hw_params_get_period_size() failed.\n");
          return err;
        }

	return 1;
}
示例#8
0
static const struct parsed_proto *check_var(FILE *fhdr,
  const char *sym, const char *varname)
{
  const struct parsed_proto *pp, *pp_sym;
  char fp_sym[256], fp_var[256], *p;
  int i;

  pp = proto_parse(fhdr, varname, 1);
  if (pp == NULL) {
    if (IS_START(varname, "sub_"))
      awarn("sub_ sym missing proto: '%s'\n", varname);
    return NULL;
  }

  if (!pp->is_func && !pp->is_fptr)
    return NULL;

  pp_print(fp_var, sizeof(fp_var), pp);

  if (pp->argc_reg == 0)
    goto check_sym;
  if (pp->argc_reg == 1 && pp->argc_stack == 0
    && IS(pp->arg[0].reg, "ecx"))
  {
    goto check_sym;
  }
  if (!g_cconv_novalidate
    && (pp->argc_reg != 2
       || !IS(pp->arg[0].reg, "ecx")
       || !IS(pp->arg[1].reg, "edx")))
  {
    awarn("unhandled reg call: %s\n", fp_var);
  }

check_sym:
  // fptrs must use 32bit args, callsite might have no information and
  // lack a cast to smaller types, which results in incorrectly masked
  // args passed (callee may assume masked args, it does on ARM)
  for (i = 0; i < pp->argc; i++) {
    if (pp->arg[i].type.is_ptr)
      continue;
    p = pp->arg[i].type.name;
    if (strstr(p, "int8") || strstr(p, "int16")
      || strstr(p, "char") || strstr(p, "short"))
    {
      awarn("reference to %s with arg%d '%s'\n", pp->name, i + 1, p);
    }
  }

  sprint_pp_short(pp, g_comment, sizeof(g_comment));

  if (sym != NULL) {
    g_func_sym_pp = NULL;
    pp_sym = proto_parse(fhdr, sym, 1);
    if (pp_sym == NULL)
      return pp;
    if (!pp_sym->is_fptr)
      aerr("func ptr data, but label '%s' !is_fptr\n", pp_sym->name);
    g_func_sym_pp = pp_sym;
  }
  else {
    pp_sym = g_func_sym_pp;
    if (pp_sym == NULL)
      return pp;
  }

  if (pp_cmp_func(pp, pp_sym)) {
    pp_print(fp_sym, sizeof(fp_sym), pp_sym);
    anote("var: %s\n", fp_var);
    anote("sym: %s\n", fp_sym);
    awarn("^ mismatch\n");
  }

  return pp;
}
示例#9
0
文件: audio_out.c 项目: Illius/libao
/* Open a device.  If this is a live device, file == NULL. */
static ao_device* _open_device(int driver_id, ao_sample_format *format,
                   ao_option *options, FILE *file)
{
    ao_functions *funcs;
    driver_list *driver;
    ao_device *device=NULL;
    int result;
        ao_sample_format sformat=*format;
        sformat.matrix=NULL;

    /* Get driver id */
    if ( (driver = _get_driver(driver_id)) == NULL ) {
        errno = AO_ENODRIVER;
        goto error;
    }

    funcs = driver->functions;

    /* Check the driver type */
    if (file == NULL &&
        funcs->driver_info()->type != AO_TYPE_LIVE) {

        errno = AO_ENOTLIVE;
        goto error;
    } else if (file != NULL &&
           funcs->driver_info()->type != AO_TYPE_FILE) {

        errno = AO_ENOTFILE;
        goto error;
    }

    /* Make a new device structure */
    if ( (device = _create_device(driver_id, driver,
                      format, file)) == NULL ) {
        errno = AO_EFAIL;
        goto error;
    }

    /* Initialize the device memory; get static channel mapping */
    if (!funcs->device_init(device)) {
        errno = AO_EFAIL;
        goto error;
    }

    /* Load options */
        errno = ao_device_load_options(device,ao_global_options);
        if(errno) goto error;
        errno = ao_device_load_options(device,options);
        if(errno) goto error;

        /* also sanitize the format input channel matrix */
        if(format->matrix){
          sformat.matrix = _sanitize_matrix(format->channels, format->matrix, device);
          if(!sformat.matrix)
            awarn("Input channel matrix invalid; ignoring.\n");

          /* special-case handling of 'M'. */
          if(sformat.channels==1 && sformat.matrix && !strcmp(sformat.matrix,"M")){
            free(sformat.matrix);
            sformat.matrix=NULL;
          }
        }

        /* If device init was able to declare a static channel mapping
           mechanism, reconcile it to the input now.  Odd drivers that
           need to communicate with a backend device to determine
           channel mapping strategy can still bypass this mechanism
           entirely.  Also, drivers like ALSA may need to adjust
           strategy depending on what device is successfully opened,
           etc, but this still saves work later. */

        if(device->output_matrix && sformat.matrix){
          switch(device->output_matrix_order){
          case AO_OUTPUT_MATRIX_FIXED:
            /* Backend channel ordering is immutable and unused
               channels must still be sent.  Look for the highest
               channel number we are able to map from the input
               matrix. */
            {
              unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix,
                                                         device->output_matrix,
                                                         &device->input_map);
              int max = _channelmask_maxbit(mask);
              if(max<0){
                aerror("Unable to map any channels from input matrix to output");
                errno = AO_EBADFORMAT;
                goto error;
              }
              device->output_channels = max+1;
              device->output_mask = mask;
              device->inter_matrix = _strdup(device->output_matrix);
            }
            break;

          case AO_OUTPUT_MATRIX_COLLAPSIBLE:
            /* the ordering of channels submitted to the backend is
               fixed, but only the channels in use should be present
               in the audio stream */
            {
              unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix,
                                                         device->output_matrix,
                                                         &device->input_map);
              int channels = _channelmask_bits(mask);
              if(channels<0){
                aerror("Unable to map any channels from input matrix to output");
                errno = AO_EBADFORMAT;
                goto error;
              }
              device->output_channels = channels;
              device->output_mask = mask;
              device->inter_matrix = _channelmask_to_matrix(mask,device->output_matrix);
            }
            break;

          case AO_OUTPUT_MATRIX_PERMUTABLE:
            /* The ordering of channels is freeform.  Only the
               channels in use should be sent, and they may be sent in
               any order.  If possible, leave the input ordering
               unchanged */
            {
              unsigned int mask = _matrix_to_channelmask(sformat.channels,sformat.matrix,
                                                         device->output_matrix,
                                                         &device->input_map);
              int channels = _channelmask_bits(mask);
              if(channels<0){
                aerror("Unable to map any channels from input matrix to output");
                errno = AO_EBADFORMAT;
                goto error;
              }
              device->output_channels = channels;
              device->output_mask = mask;
              device->inter_matrix = _matrix_intersect(sformat.matrix,device->output_matrix);
            }
            break;

          default:
            aerror("Driver backend failed to set output ordering.\n");
            errno = AO_EFAIL;
            goto error;
          }

        }else{
          device->output_channels = sformat.channels;
        }

        /* other housekeeping */
        device->input_channels = sformat.channels;
        device->bytewidth = (sformat.bits+7)>>3;
        device->rate = sformat.rate;

    /* Open the device */
    result = funcs->open(device, &sformat);
    if (!result) {
          errno = AO_EOPENDEVICE;
          goto error;
    }

        /* set up permutation based on finalized inter_matrix mapping */
        /* The only way device->output_channels could be zero here is
           if the driver has opted to ouput no channels (eg, the null
           driver). */
        if(sformat.matrix){
          if(!device->inter_matrix){
            awarn("Driver %s does not support automatic channel mapping;\n"
                 "\tRouting only L/R channels to output.\n\n",
                 info_table[device->driver_id]->short_name);
            device->inter_matrix=_strdup("L,R");
          }
          {

            /* walk thorugh the inter matrix, match channels */
            char *op=device->inter_matrix;
            int count=0;
            device->inter_permute = calloc(device->output_channels,sizeof(int));

            adebug("\n");

            while(count<device->output_channels){
              int m=0,mm;
              char *h=op;

              if(*op){
                /* find mnemonic offset of inter channel */
                while(*h && *h!=',')h++;
                while(mnemonics[m]){
                  if(!strncmp(mnemonics[m],op,h-op))
                    break;
                  m++;
                }
                mm=m;

                /* find match in input if any */
                device->inter_permute[count] = _find_channel(m,sformat.matrix);
                if(device->inter_permute[count] == -1 && sformat.channels == 1){
                  device->inter_permute[count] = _find_channel(1,sformat.matrix);
                  mm=1;
                }
              }else
                device->inter_permute[count] = -1;

              /* display resulting mapping for now */
              if(device->inter_permute[count]>=0){
                adebug("input %d (%s)\t -> backend %d (%s)\n",
                       device->inter_permute[count], mnemonics[mm],
                       count,mnemonics[m]);
              }else{
                adebug("             \t    backend %d (%s)\n",
                       count,mnemonics[m]);
              }
              count++;
              op=h;
              if(*h)op++;
            }
            {
              char **inch = _tokenize_matrix(sformat.matrix);
              int i,j;
              int unflag=0;
              for(j=0;j<sformat.channels;j++){
                for(i=0;i<device->output_channels;i++)
                  if(device->inter_permute[i]==j)break;
                if(i==device->output_channels){
                  adebug("input %d (%s)\t -> none\n",
                         j,inch[j]);
                  unflag=1;
                }
              }
              _free_map(inch);
              if(unflag)
                awarn("Some input channels are unmapped and will not be used.\n");
            }
            averbose("\n");

          }
        }

        /* if there's no actual permutation to do, release the permutation vector */
        if(device->inter_permute && device->output_channels == device->input_channels){
          int i;
          for(i=0;i<device->output_channels;i++)
            if(device->inter_permute[i]!=i)break;
          if(i==device->output_channels){
            free(device->inter_permute);
            device->inter_permute=NULL;
          }
        }

    /* Resolve actual driver byte format */
    device->driver_byte_format =
        _real_byte_format(device->driver_byte_format);

    /* Only create swap buffer if needed */
        if (device->bytewidth>1 &&
            device->client_byte_format != device->driver_byte_format){
          adebug("swap buffer required:\n");
          adebug("  machine endianness: %d\n",ao_is_big_endian());
          adebug("  device->client_byte_format:%d\n",device->client_byte_format);
          adebug("  device->driver_byte_format:%d\n",device->driver_byte_format);
        }

    if ( (device->bytewidth>1 &&
              device->client_byte_format != device->driver_byte_format) ||
             device->inter_permute){

          result = _realloc_swap_buffer(device, DEF_SWAP_BUF_SIZE);

          if (!result) {

            if(sformat.matrix)free(sformat.matrix);
            device->funcs->close(device);
            device->funcs->device_clear(device);
            free(device);
            errno = AO_EFAIL;
            return NULL; /* Couldn't alloc swap buffer */
          }
    }

    /* If we made it this far, everything is OK. */
        if(sformat.matrix)free(sformat.matrix);
    return device;

 error:
        {
          int errtemp = errno;
          if(sformat.matrix)
            free(sformat.matrix);
          ao_close(device);
          errno=errtemp;
        }
        return NULL;
}
示例#10
0
int ao_plugin_close(ao_device *device)
{
  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
  OSStatus status;
  UInt32 sizeof_running,running;

  // If we never got started and there's data waiting to be played,
  // start now.

  pthread_mutex_lock(&mutex);

  if(internal->output_p){
    internal->output_p=0;
    internal->isStopping = true;

    if(!internal->started && internal->validByteCount){
      status = AudioOutputUnitStart(internal->outputAudioUnit);
      adebug("Starting audio output unit\n");
      if(status){
        pthread_mutex_unlock(&mutex);
        aerror("Failed to start audio output => %d\n",(int)status);
        return 0;
      }
      internal->started = true;
    }

    // For some rare cases (using atexit in your program) Coreaudio tears
    // down the HAL itself, so we do not need to do that here.
    // We wouldn't get an error if we did, but AO would hang waiting for the 
    // AU to flush the buffer
    sizeof_running = sizeof(UInt32);
    AudioUnitGetProperty(internal->outputAudioUnit, 
                         kAudioDevicePropertyDeviceIsRunning,
                         kAudioUnitScope_Input,
                         0,
                         &running, 
                         &sizeof_running);

    if (!running) {
      pthread_mutex_unlock(&mutex);
      return 1;
    }

    // Only stop if we ever got started
    if (internal->started) {

      // Wait for any pending data to get flushed
      while (internal->validByteCount)
        pthread_cond_wait(&cond, &mutex);

      pthread_mutex_unlock(&mutex);
    
      status = AudioOutputUnitStop(internal->outputAudioUnit);
      if (status) {
        awarn("AudioOutputUnitStop returned %d\n", (int)status);
        return 0;
      }

      status = AudioComponentInstanceDispose(internal->outputAudioUnit);
      if (status) {
        awarn("AudioComponentInstanceDispose returned %d\n", (int)status);
        return 0;
      }
    }else
      pthread_mutex_unlock(&mutex);
  }else
    pthread_mutex_unlock(&mutex);

  return 1;
}
示例#11
0
int ao_plugin_open(ao_device *device, ao_sample_format *format)
{
  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
  OSStatus result = noErr;
  AudioComponent comp;
  AudioComponentDescription desc;
  AudioStreamBasicDescription requestedDesc;
  AURenderCallbackStruct      input;
  UInt32 i_param_size, requestedEndian;

  /* Locate the default output audio unit */
  desc.componentType = kAudioUnitType_Output;
  desc.componentSubType = kAudioUnitSubType_HALOutput;
  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  desc.componentFlags = 0;
  desc.componentFlagsMask = 0;

  comp = AudioComponentFindNext (NULL, &desc);
  if (comp == NULL) {
    aerror("Failed to start CoreAudio: AudioComponentFindNext returned NULL");
    return 0;
  }

  /* Open & initialize the default output audio unit */
  result = AudioComponentInstanceNew (comp, &internal->outputAudioUnit);
  if (result) {
    aerror("AudioComponentInstanceNew() error => %d\n",(int)result);
    return 0;
  }
  /* Set the desired output device if not default */
  if (internal->outputDevice != kAudioObjectUnknown) {
    result = AudioUnitSetProperty (internal->outputAudioUnit,
                                   kAudioOutputUnitProperty_CurrentDevice,
                                   kAudioUnitScope_Global,
                                   0,
                                   &internal->outputDevice,
                                   sizeof(internal->outputDevice));
    if (result) {
      aerror("AudioComponentSetDevice() error => %d\n",(int)result);
      AudioComponentInstanceDispose(internal->outputAudioUnit);
      return 0;
    }
  }
  internal->output_p=1;

  /* Request desired format of the audio unit.  Let HAL do all
     conversion since it will probably be doing some internal
     conversion anyway. */

  device->driver_byte_format = format->byte_format;
  requestedDesc.mFormatID = kAudioFormatLinearPCM;
  requestedDesc.mFormatFlags = kAudioFormatFlagIsPacked;
  switch(format->byte_format){
  case AO_FMT_BIG:
    requestedDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
    break;
  case AO_FMT_NATIVE:
    if(ao_is_big_endian())
      requestedDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
    break;
  }
  requestedEndian = requestedDesc.mFormatFlags & kAudioFormatFlagIsBigEndian;
  if (format->bits > 8)
    requestedDesc.mFormatFlags |= kAudioFormatFlagIsSignedInteger;

  requestedDesc.mChannelsPerFrame = device->output_channels;
  requestedDesc.mSampleRate = format->rate;
  requestedDesc.mBitsPerChannel = format->bits;
  requestedDesc.mFramesPerPacket = 1;
  requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
  requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;

  result = AudioUnitSetProperty (internal->outputAudioUnit,
				 kAudioUnitProperty_StreamFormat,
				 kAudioUnitScope_Input,
				 0,
				 &requestedDesc,
				 sizeof(requestedDesc));

  if (result) {
    aerror("AudioUnitSetProperty error => %d\n",(int)result);
    return 0;
  }

  /* what format did we actually get? */
  i_param_size = sizeof(requestedDesc);
  result = AudioUnitGetProperty(internal->outputAudioUnit,
			     kAudioUnitProperty_StreamFormat,
			     kAudioUnitScope_Input,
			     0,
			     &requestedDesc,
			     &i_param_size );
  if (result) {
    aerror("Failed to query modified device hardware settings => %d\n",(int)result);
    return 0;
  }

  /* If any major settings differ, abort */
  if(fabs(requestedDesc.mSampleRate-format->rate) > format->rate*.05){
    aerror("Unable to set output sample rate\n");
    return 0;
  }
  if(requestedDesc.mChannelsPerFrame != device->output_channels){
    aerror("Could not configure %d channel output\n",device->output_channels);
    return 0;
  }
  if(requestedDesc.mBitsPerChannel != format->bits){
    aerror("Could not configure %d bit output\n",format->bits);
    return 0;
  }
  if(requestedDesc.mBitsPerChannel != format->bits){
    aerror("Could not configure %d bit output\n",format->bits);
    return 0;
  }
  if(requestedDesc.mFormatFlags & kAudioFormatFlagIsFloat){
    aerror("Could not configure integer sample output\n");
    return 0;
  }
  if((requestedDesc.mFormatFlags & kAudioFormatFlagsNativeEndian) !=
     requestedEndian){
    aerror("Could not configure output endianness\n");
    return 0;
  }

  if (format->bits > 8){
    if(!(requestedDesc.mFormatFlags & kAudioFormatFlagIsSignedInteger)){
      aerror("Could not configure signed output\n");
      return 0;
    }
  }else{
    if((requestedDesc.mFormatFlags & kAudioFormatFlagIsSignedInteger)){
      aerror("Could not configure unsigned output\n");
      return 0;
    }
  }
  if(requestedDesc.mSampleRate != format->rate){
    awarn("Could not set sample rate to exactly %d; using %g instead.\n",
	  format->rate,(double)requestedDesc.mSampleRate);
  }

  /* set the channel mapping.  MacOSX AUHAL is capable of mapping any
     channel format currently representable in the libao matrix. */

  if(device->output_mask){
    AudioChannelLayout layout;
    memset(&layout,0,sizeof(layout));

    layout.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap;
    layout.mChannelBitmap = device->output_mask;

    result = AudioUnitSetProperty(internal->outputAudioUnit,
				  kAudioUnitProperty_AudioChannelLayout,
				  kAudioUnitScope_Input, 0, &layout, sizeof(layout));
    if (result) {
      aerror("Failed to set audio channel layout => %d\n", (int)result);
    }
  }

  /* Set the audio callback */
  input.inputProc = (AURenderCallback) audioCallback;
  input.inputProcRefCon = internal;

  result = AudioUnitSetProperty( internal->outputAudioUnit,
				 kAudioUnitProperty_SetRenderCallback,
				 kAudioUnitScope_Input,
				 0, &input, sizeof( input ));


  if (result) {
    aerror("Callback set error => %d\n",(int)result);
    return 0;
  }

  result = AudioUnitInitialize (internal->outputAudioUnit);
  if (result) {
    aerror("AudioUnitInitialize() error => %d\n",(int)result);
    return 0;
  }

  /* Since we don't know how big to make the buffer until we open the device
     we allocate the buffer here instead of ao_plugin_device_init() */

  internal->bufferByteCount =  (internal->buffer_time * format->rate / 1000) *
    (device->output_channels * format->bits / 8);

  internal->firstValidByteOffset = 0;
  internal->validByteCount = 0;
  internal->buffer = malloc(internal->bufferByteCount);
  memset(internal->buffer, 0, internal->bufferByteCount);
  if (!internal->buffer) {
    aerror("Unable to allocate queue buffer.\n");
    return 0;
  }

  /* limited to stereo for now */
  //if(!device->output_matrix)
  //device->output_matrix=strdup("L,R");

  return 1;
}