/* 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; }
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; }
/* 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"); }
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; }
/* 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; }
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; }
/* 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(¶ms); /* 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; }
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; }
/* 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; }
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; }
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; }