/* If *name is a valid driver name, return its driver number. Otherwise, test all of available live drivers until one works. */ static int _find_default_driver_id (const char *name) { int def_id; int id; ao_info *info; driver_list *dl = driver_head; ao_device *device = ao_global_dummy; adebug("Testing drivers to find playback default...\n"); if ( name == NULL || (def_id = ao_driver_id(name)) < 0 ) { /* No default specified. Find one among available drivers. */ def_id = -1; id = 0; while (dl != NULL) { info = dl->functions->driver_info(); adebug("...testing %s\n",info->short_name); if ( info->type == AO_TYPE_LIVE && info->priority > 0 && /* Skip static drivers */ dl->functions->test() ) { def_id = id; /* Found a usable driver */ adebug("OK, using driver %s\n",info->short_name); break; } dl = dl->next; id++; } } return def_id; }
int ao_plugin_play(ao_device *device, const char *output_samples, uint_32 num_bytes) { ao_macosx_internal *internal = (ao_macosx_internal *) device->internal; int err; unsigned int bytesToCopy; unsigned int firstEmptyByteOffset, emptyByteCount; while (num_bytes) { // Get a consistent set of data about the available space in the queue, // figure out the maximum number of bytes we can copy in this chunk, // and claim that amount of space pthread_mutex_lock(&mutex); // Wait until there is some empty space in the queue emptyByteCount = internal->bufferByteCount - internal->validByteCount; while (emptyByteCount == 0) { if(!internal->started){ err = AudioOutputUnitStart(internal->outputAudioUnit); adebug("Starting audio output unit\n"); if(err){ pthread_mutex_unlock(&mutex); aerror("Failed to start audio output => %d\n",(int)err); return 0; } internal->started = true; } err = pthread_cond_wait(&cond, &mutex); if (err) adebug("pthread_cond_wait() => %d\n",err); emptyByteCount = internal->bufferByteCount - internal->validByteCount; } // Compute the offset to the first empty byte and the maximum number of // bytes we can copy given the fact that the empty space might wrap // around the end of the queue. firstEmptyByteOffset = (internal->firstValidByteOffset + internal->validByteCount) % internal->bufferByteCount; if (firstEmptyByteOffset + emptyByteCount > internal->bufferByteCount) bytesToCopy = MIN(num_bytes, internal->bufferByteCount - firstEmptyByteOffset); else bytesToCopy = MIN(num_bytes, emptyByteCount); // Copy the bytes and get ready for the next chunk, if any memcpy(internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy); num_bytes -= bytesToCopy; output_samples += bytesToCopy; internal->validByteCount += bytesToCopy; pthread_mutex_unlock(&mutex); } return 1; }
int ao_wmm_set_option(ao_device *device, const char *key, const char *value) { ao_wmm_internal *internal = (ao_wmm_internal *) device->internal; int res = 0; if (!strcmp(key, "dev")) { if (!strcmp(value,"default")) { key = "id"; value = "0"; } else { WAVEOUTCAPS caps; int i, max = waveOutGetNumDevs(); adebug("searching for device %s among %d\n", value, max); for (i=0; i<max; ++i) { MMRESULT mmres = waveOutGetDevCaps(i, &caps, sizeof(caps)); if (mmres == MMSYSERR_NOERROR) { res = !strcmp(value, caps.szPname); adebug("checking id=%d, name='%s', ver=%d.%d => [%s]\n", i,caps.szPname,caps.vDriverVersion>>8,caps.vDriverVersion&255,res?"YES":"no"); if (res) { internal->id = i; internal->caps = caps; break; } } else { aerror("waveOutGetDevCaps(%d) => %s",i,mmerror(mmres)); } } goto finish; }
/* recover from an alsa exception */ static inline int alsa_error_recovery(ao_alsa_internal *internal, int err, ao_device *device) { if (err == -EPIPE) { /* FIXME: underrun length detection */ adebug("underrun, restarting...\n"); /* output buffer underrun */ err = snd_pcm_prepare(internal->pcm_handle); if (err < 0) return err; } else if (err == -ESTRPIPE) { /* application was suspended, wait until suspend flag clears */ while ((err = snd_pcm_resume(internal->pcm_handle)) == -EAGAIN) sleep (1); if (err < 0) { /* unable to wake up pcm device, restart it */ err = snd_pcm_prepare(internal->pcm_handle); if (err < 0) return err; } return 0; } /* error isn't recoverable */ return err; }
static int ao_null_close(ao_device *device) { ao_null_internal *internal = (ao_null_internal *) device->internal; adebug("%ld bytes sent to null device.\n", internal->byte_counter); return 1; }
/* Convert the static drivers table into a linked list of drivers. */ static driver_list* _load_static_drivers(driver_list **end) { ao_device *device = ao_global_dummy; driver_list *head; driver_list *driver; int i; /* insert first driver */ head = driver = calloc(1,sizeof(driver_list)); if (driver != NULL) { driver->functions = static_drivers[0]; driver->handle = NULL; driver->next = NULL; adebug("Loaded driver %s (built-in)\n",driver->functions->driver_info()->short_name); i = 1; while (static_drivers[i] != NULL) { driver->next = calloc(1,sizeof(driver_list)); if (driver->next == NULL) break; driver->next->functions = static_drivers[i]; driver->next->handle = NULL; driver->next->next = NULL; driver = driver->next; adebug("Loaded driver %s (built-in)\n",driver->functions->driver_info()->short_name); i++; } } if (end != NULL) *end = driver; return head; }
static int ao_device_load_options(ao_device *device, ao_option *options){ while (options != NULL) { if(!strcmp(options->key,"matrix")){ /* If a driver has a static channel mapping mechanism (physically constant channel mapping, or at least an unvarying set of constants for mapping channels), the output_matrix is already set. An app/user specified output mapping trumps. */ if(device->output_matrix) free(device->output_matrix); /* explicitly set the output matrix to the requested string; devices must not override. */ device->output_matrix = _sanitize_matrix(32, options->value, device); if(!device->output_matrix){ aerror("Empty or inavlid output matrix\n"); return AO_EBADOPTION; } adebug("Sanitized device output matrix: %s\n",device->output_matrix); }else if(!strcmp(options->key,"debug")){ device->verbose=2; }else if(!strcmp(options->key,"verbose")){ if(device->verbose<1)device->verbose=1; }else if(!strcmp(options->key,"quiet")){ device->verbose=-1; }else{ if (!device->funcs->set_option(device, options->key, options->value)) { /* Problem setting options */ return AO_EOPENDEVICE; } } options = options->next; } return 0; }
/* Load the dynamic drivers from disk and append them to end of the driver list. end points the driver_list node to append to. */ static void _append_dynamic_drivers(driver_list *end) { #ifdef HAVE_DLOPEN struct dirent *plugin_dirent; char *ext; struct stat statbuf; DIR *plugindir; driver_list *plugin; driver_list *driver = end; ao_device *device = ao_global_dummy; /* now insert any plugins we find */ plugindir = opendir(AO_PLUGIN_PATH); adebug("Loading driver plugins from %s...\n",AO_PLUGIN_PATH); if (plugindir != NULL) { while ((plugin_dirent = readdir(plugindir)) != NULL) { char fullpath[strlen(AO_PLUGIN_PATH) + 1 + strlen(plugin_dirent->d_name) + 1]; snprintf(fullpath, sizeof(fullpath), "%s/%s", AO_PLUGIN_PATH, plugin_dirent->d_name); if (!stat(fullpath, &statbuf) && S_ISREG(statbuf.st_mode) && (ext = strrchr(plugin_dirent->d_name, '.')) != NULL) { if (strcmp(ext, SHARED_LIB_EXT) == 0) { plugin = _get_plugin(fullpath); if (plugin) { driver->next = plugin; plugin->next = NULL; driver = driver->next; } } } } closedir(plugindir); } #endif }
/* Work around PulseAudio ALSA plugin bug where the PA server forces a higher than requested latency, but the plugin does not update its (and ALSA's) internal state to reflect that, leading to an immediate underrun situation. Inspired by WINE's make_handle_underrun_config. Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/053391.html From Mozilla https://github.com/kinetiknz/cubeb/blob/1aa0058d0729eb85505df104cd1ac072432c6d24/src/cubeb_alsa.c */ static snd_config_t *init_local_config_with_workaround(ao_device *device, char const *name){ char pcm_node_name[80]; int r; snd_config_t * lconf; snd_config_t * device_node; snd_config_t * type_node; snd_config_t * node; char const * type_string; lconf = NULL; snprintf(pcm_node_name,80,"pcm.%s",name); if (snd_config == NULL) snd_config_update(); r = snd_config_copy(&lconf, snd_config); if(r<0){ return NULL; } r = snd_config_search(lconf, pcm_node_name, &device_node); if (r != 0) { snd_config_delete(lconf); return NULL; } /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */ r = snd_config_search(device_node, "type", &type_node); if (r != 0) { snd_config_delete(lconf); return NULL; } r = snd_config_get_string(type_node, &type_string); if (r != 0) { snd_config_delete(lconf); return NULL; } if (strcmp(type_string, "pulse") != 0) { snd_config_delete(lconf); return NULL; } /* Don't clobber an explicit existing handle_underrun value, set it only if it doesn't already exist. */ r = snd_config_search(device_node, "handle_underrun", &node); if (r != -ENOENT) { snd_config_delete(lconf); return NULL; } r = snd_config_imake_integer(&node, "handle_underrun", 0); if (r != 0) { snd_config_delete(lconf); return NULL; } r = snd_config_add(device_node, node); if (r != 0) { snd_config_delete(lconf); return NULL; } adebug("PulseAudio ALSA-emulation detected: disabling underrun detection\n"); return lconf; }
/* * open the audio device for writing to */ int ao_plugin_open(ao_device *device, ao_sample_format *format) { ao_oss_internal *internal = (ao_oss_internal *) device->internal; int tmp; /* Open the device driver */ if (internal->dev != NULL) { /* open the user-specified path */ internal->fd = open(internal->dev, O_WRONLY); if(internal->fd < 0) { aerror("open(%s) => %s\n",internal->dev,strerror(errno)); return 0; /* Cannot open device */ } } else { internal->fd = _open_default_oss_device(&internal->dev, internal->id, 1); if (internal->fd < 0) { aerror("open default => %s\n",strerror(errno)); return 0; /* Cannot open default device */ } } /* try to lower the DSP delay; this ioctl may fail gracefully */ { long bytesperframe=(format->bits+7)/8*device->output_channels*format->rate*(internal->buffer_time/1000000.); int fraglog=ilog(bytesperframe); int fragment=0x00040000|fraglog,fragcheck; fragcheck=fragment; int ret=ioctl(internal->fd,SNDCTL_DSP_SETFRAGMENT,&fragment); if(ret || fragcheck!=fragment) { fprintf(stderr,"Could not set DSP fragment size; continuing.\n"); } } /* Now set all of the parameters */ #ifdef SNDCTL_DSP_CHANNELS /* OSS versions > 2 */ tmp = device->output_channels; if (ioctl(internal->fd,SNDCTL_DSP_CHANNELS,&tmp) < 0 || tmp != device->output_channels) { aerror("cannot set channels to %d\n", device->output_channels); goto ERR; } #else /* OSS versions < 4 */ switch (device->output_channels) { case 1: tmp = 0; break; case 2: tmp = 1; break; default: aerror("Unsupported number of channels: %d.\n", device->output_channels); goto ERR; } if (ioctl(internal->fd,SNDCTL_DSP_STEREO,&tmp) < 0 || tmp+1 != device->output_channels) { aerror("cannot set channels to %d\n", device->output_channels); goto ERR; } #endif /* To eliminate the need for a swap buffer, we set the device to use whatever byte format the client selected. */ switch (format->bits) { case 8: tmp = AFMT_S8; break; case 16: tmp = device->client_byte_format == AO_FMT_BIG ? AFMT_S16_BE : AFMT_S16_LE; device->driver_byte_format = device->client_byte_format; break; default: aerror("Unsupported number of bits: %d.", format->bits); goto ERR; } if (ioctl(internal->fd,SNDCTL_DSP_SAMPLESIZE,&tmp) < 0) { aerror("cannot set sample size to %d\n", format->bits); goto ERR; } tmp = format->rate; /* Some cards aren't too accurate with their clocks and set to the exact data rate, but something close. Fail only if completely out of whack. */ if (ioctl(internal->fd,SNDCTL_DSP_SPEED, &tmp) < 0 || tmp > 1.02 * format->rate || tmp < 0.98 * format->rate) { aerror("cannot set rate to %d\n", format->rate); goto ERR; } /* this calculates and sets the fragment size */ internal->buf_size = -1; if ((ioctl(internal->fd,SNDCTL_DSP_GETBLKSIZE, &(internal->buf_size)) < 0) || internal->buf_size<=0 ) { /* Some versions of the *BSD OSS drivers use a subtly different SNDCTL_DSP_GETBLKSIZE ioctl implementation which is, oddly, incompatible with the shipped declaration in soundcard.h. This ioctl isn't necessary anyway, it's just tuning. Soldier on without, */ adebug("cannot get buffer size for device; using a default of 1024kB\n"); internal->buf_size=1024; } if(!device->inter_matrix) { /* set up matrix such that users are warned about > stereo playback */ if(device->output_channels<=2) device->inter_matrix=strdup("L,R"); //else no matrix, which results in a warning } return 1; /* Open successful */ ERR: close(internal->fd); return 0; /* Failed to open device */ }
/* Load a plugin from disk and put the function table into a driver_list struct. */ static driver_list *_get_plugin(char *plugin_file) { ao_device *device = ao_global_dummy; driver_list *dt; void *handle; char *prompt=""; handle = dlopen(plugin_file, DLOPEN_FLAG /* See ao_private.h */); if (handle) { prompt="calloc() failed"; dt = (driver_list *)calloc(1,sizeof(driver_list)); if (!dt) return NULL; dt->handle = handle; dt->functions = (ao_functions *)calloc(1,sizeof(ao_functions)); if (!(dt->functions)) { free(dt); return NULL; } prompt="ao_plugin_test() missing"; dt->functions->test = dlsym(dt->handle, "ao_plugin_test"); if (!(dt->functions->test)) goto failed; prompt="ao_plugin_driver_info() missing"; dt->functions->driver_info = dlsym(dt->handle, "ao_plugin_driver_info"); if (!(dt->functions->driver_info)) goto failed; prompt="ao_plugin_device_list() missing"; dt->functions->device_init = dlsym(dt->handle, "ao_plugin_device_init"); if (!(dt->functions->device_init )) goto failed; prompt="ao_plugin_set_option() missing"; dt->functions->set_option = dlsym(dt->handle, "ao_plugin_set_option"); if (!(dt->functions->set_option)) goto failed; prompt="ao_plugin_open() missing"; dt->functions->open = dlsym(dt->handle, "ao_plugin_open"); if (!(dt->functions->open)) goto failed; prompt="ao_plugin_play() missing"; dt->functions->play = dlsym(dt->handle, "ao_plugin_play"); if (!(dt->functions->play)) goto failed; prompt="ao_plugin_close() missing"; dt->functions->close = dlsym(dt->handle, "ao_plugin_close"); if (!(dt->functions->close)) goto failed; prompt="ao_plugin_clear() missing"; dt->functions->device_clear = dlsym(dt->handle, "ao_plugin_device_clear"); if (!(dt->functions->device_clear)) goto failed; } else { aerror("Failed to load plugin %s => dlopen() failed\n",plugin_file); return NULL; } adebug("Loaded driver %s\n",dt->functions->driver_info()->short_name); return dt; failed: aerror("Failed to load plugin %s => %s\n",plugin_file,prompt); free(dt->functions); free(dt); return NULL; }
/* 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 transfer behavior */ static inline int alsa_set_swparams(ao_device *device) { ao_alsa_internal *internal = (ao_alsa_internal *) device->internal; snd_pcm_sw_params_t *params; snd_pcm_uframes_t boundary; int err; /* allocate the software parameter structure */ snd_pcm_sw_params_alloca(¶ms); /* fetch the current software parameters */ err = snd_pcm_sw_params_current(internal->pcm_handle, params); if (err < 0){ adebug("snd_pcm_sw_params_current() failed.\n"); return err; } #if 0 /* the below causes more trouble than it cures */ /* allow transfers to start when there is one period */ err = snd_pcm_sw_params_set_start_threshold(internal->pcm_handle, params, internal->period_size); if (err < 0){ adebug("snd_pcm_sw_params_set_start_threshold() failed.\n"); //return err; } /* require a minimum of one full transfer in the buffer */ err = snd_pcm_sw_params_set_avail_min(internal->pcm_handle, params, internal->period_size); if (err < 0){ adebug("snd_pcm_sw_params_set_avail_min() failed.\n"); //return err; } #endif /* do not align transfers; this is obsolete/deprecated in ALSA 1.x where the transfer alignemnt is always 1 (except for buggy drivers like VIA 82xx which still demand aligned transfers regardless of setting, in violation of the ALSA API docs) */ err = snd_pcm_sw_params_set_xfer_align(internal->pcm_handle, params, 1); if (err < 0){ adebug("snd_pcm_sw_params_set_xfer_align() failed.\n"); //return err; } /* get the boundary size */ err = snd_pcm_sw_params_get_boundary(params,&boundary); if (err < 0){ adebug("snd_pcm_sw_params_get_boundary() failed.\n"); }else{ /* force a work-ahead silence buffer; this is a fix, again for VIA 82xx, where non-MMIO transfers will buffer into period-size transfers, but the last transfer is usually undersized and playback falls off the end of the submitted data. */ err = snd_pcm_sw_params_set_silence_size(internal->pcm_handle, params, boundary); if (err < 0){ adebug("snd_pcm_sw_params_set_silence_size() failed.\n"); //return err; } } /* commit the params structure to ALSA */ err = snd_pcm_sw_params(internal->pcm_handle, params); if (err < 0){ adebug("snd_pcm_sw_params() failed.\n"); return err; } return 1; }
/* 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; }
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; }
/* 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; }