static ao_device * audio_open_device(shairplay_options_t *opt, int bits, int channels, int samplerate) { ao_device *device = NULL; ao_option *ao_options = NULL; ao_sample_format format; int driver_id; /* Get the libao driver ID */ if (strlen(opt->ao_driver)) { driver_id = ao_driver_id(opt->ao_driver); } else { driver_id = ao_default_driver_id(); } /* Add all available libao options */ if (strlen(opt->ao_devicename)) { ao_append_option(&ao_options, "dev", opt->ao_devicename); } if (strlen(opt->ao_deviceid)) { ao_append_option(&ao_options, "id", opt->ao_deviceid); } /* Set audio format */ memset(&format, 0, sizeof(format)); format.bits = bits; format.channels = channels; format.rate = samplerate; format.byte_format = AO_FMT_NATIVE; /* Try opening the actual device */ device = ao_open_live(driver_id, &format, ao_options); ao_free_options(ao_options); return device; }
void init_ao( session_t *sess ) { if( ao_thread != 0 ) return; ao_initialize(); output_buffer_size = 4 * (sess->frame_size+3); output_buffer = malloc(output_buffer_size); int driver; if (libao_driver) { // if a libao driver is specified on the command line, use that driver = ao_driver_id(libao_driver); if (driver == -1) { debugp( DEBUGP_DEFAULT, 0, "Could not find requested ao driver" ); kill( getpid(), SIGINT ); } } else { // otherwise choose the default driver = ao_default_driver_id(); } ao_sample_format fmt; memset(&fmt, 0, sizeof(fmt)); fmt.bits = 16; fmt.rate = sess->sample_rate; fmt.channels = NUM_CHANNELS; fmt.byte_format = AO_FMT_NATIVE; ao_option *ao_opts = NULL; if(libao_deviceid) { ao_append_option(&ao_opts, "id", libao_deviceid); } else if(libao_devicename){ ao_append_option(&ao_opts, "dev", libao_devicename); // Old libao versions (for example, 0.8.8) only support // "dsp" instead of "dev". ao_append_option(&ao_opts, "dsp", libao_devicename); } dev = ao_open_live(driver, &fmt, ao_opts); if (dev == NULL) { debugp( DEBUGP_DEFAULT, 0, "Could not open ao device (%d)", errno); kill( getpid(), SIGINT ); } if(pthread_create( &ao_thread, NULL, ao_thread_func, NULL )) { debugp( DEBUGP_DEFAULT, 0, "Could not start ao_thread\n" ); kill( getpid(), SIGINT ); } return; }
int fm_player_open(fm_player_t *pl, fm_player_config_t *config) { pl->config = *config; if (strcmp(config->driver, "pifm") == 0) { float f = atof(config->dev); if (f < 1) f = 102.4; printf("Player audio driver: pifm\n"); printf("Player sample rate: %d Hz\n", config->rate); printf("Player FM fequency: %f Hz\n", f); config->channels = 1; fm_setup_fm(); fm_setup_dma(f); } else { ao_sample_format ao_fmt; ao_fmt.rate = config->rate; ao_fmt.channels = config->channels; ao_fmt.bits = mpg123_encsize(config->encoding) * 8; ao_fmt.byte_format = AO_FMT_NATIVE; ao_fmt.matrix = 0; int driver = ao_driver_id(config->driver); if (driver == -1) { return -1; } ao_info *driver_info = ao_driver_info(driver); printf("Player audio driver: %s\n", driver_info->name); printf("Player sample rate: %d Hz\n", pl->config.rate); ao_option *options = NULL; if (config->dev[0] != '\0') { ao_append_option(&options, "dev", config->dev); } pl->dev = ao_open_live(driver, &ao_fmt, options); ao_free_options(options); if (pl->dev == NULL) return -1; } pl->mh = mpg123_new(NULL, NULL); mpg123_format_none(pl->mh); mpg123_format(pl->mh, config->rate, config->channels, config->encoding); pl->curl = curl_easy_init(); curl_easy_setopt(pl->curl, CURLOPT_WRITEFUNCTION, download_callback); curl_easy_setopt(pl->curl, CURLOPT_WRITEDATA, pl); pl->tid_ack = 0; pthread_mutex_init(&pl->mutex_status, NULL); pthread_cond_init(&pl->cond_play, NULL); pl->status = FM_PLAYER_STOP; return 0; }
static ao_option * dict_to_options(PyObject *dict) { Py_ssize_t pos = 0; PyObject *key, *val; ao_option *head = NULL; int ret; if (!PyDict_Check(dict)) { PyErr_SetString(PyExc_TypeError, "Must be a dictionary"); return NULL; } while (PyDict_Next(dict, &pos, &key, &val) > 0) { if (!PyString_Check(key) || !PyString_Check(val)) { PyErr_SetString(PyExc_TypeError, "Option keys may only be strings"); goto error; } ret = ao_append_option(&head, PyString_AsString(key), PyString_AsString(val)); if (ret == 0) { PyErr_SetString(Py_aoError, "Error appending options"); goto error; } } return head; error: ao_free_options(head); return NULL; }
static int parse_driver_options( const char *device, int *driver_id, ao_option **options ) { char *mutable, *option, *key, *value; /* Get a copy of the device string we can modify */ if( !device || *device == '\0' ) return 0; mutable = strdup( device ); if( !mutable ) { ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ ); return 1; } /* First, find the device name */ option = strchr( mutable, ':' ); if( option ) *option++ = '\0'; if( *mutable ) /* ! \0 */ *driver_id = ao_driver_id( mutable ); /* Now parse any further options */ while( option ) { key = option; option = strchr( option, ',' ); if( option ) *option++ = '\0'; value = strchr( key, '=' ); if( value ) *value++ = '\0'; if( strcmp( key, "file" ) == 0 ) { filename = strdup( value ); if( !filename ) { ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ ); free( mutable ); return 1; } } else if( key && value) { ao_append_option( options, key, value ); } else if ( first_init ) { ui_error( UI_ERROR_ERROR, "ignoring badly formed libao option (%s%s)", key ? key : "", value ? value : "" ); } } free( mutable ); return 0; }
void process_config_options(const struct uade_config *uc) { char *s; char *key; char *value; if (uc->buffer_time > 0) { char val[32]; /* buffer_time is given in milliseconds, so convert to microseconds */ snprintf(val, sizeof val, "%d", 1000 * uc->buffer_time); ao_append_option(&options, "buffer_time", val); } format.bits = UADE_BYTES_PER_SAMPLE * 8; format.channels = UADE_CHANNELS; format.rate = uc->frequency; format.byte_format = AO_FMT_NATIVE; s = (char *) uc->ao_options.o; while (s != NULL && *s != 0) { key = s; s = strchr(s, '\n'); if (s == NULL) break; *s = 0; s++; value = strchr(key, ':'); if (value == NULL) { __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "uade: Invalid ao option: %s\n", key); continue; } *value = 0; value++; ao_append_option(&options, key, value); } }
static void add_driver_option(const char *key_value) { char buf[1024]; char *value = NULL; char *sep; strncpy(buf, key_value, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; sep = strchr(buf, ':'); if (sep) { *sep = '\0'; value = sep + 1; } ao_append_option(&device_options, buf, value); }
int add_ao_option(ao_option **op_h, const char *optstring) { char *key, *value; int result; key = strdup(optstring); if (key == NULL) return 0; value = strchr(key, ':'); if (value) { /* split by replacing the separator with a null */ *value++ = '\0'; } result = ao_append_option(op_h, key, value); free(key); return (result); }
static struct audio_output * ao_output_init(const struct config_param *param, GError **error) { struct ao_data *ad = g_new(struct ao_data, 1); if (!ao_base_init(&ad->base, &ao_output_plugin, param, error)) { g_free(ad); return NULL; } ao_info *ai; const char *value; ad->options = NULL; ad->write_size = config_get_block_unsigned(param, "write_size", 1024); if (ao_output_ref == 0) { ao_initialize(); } ao_output_ref++; value = config_get_block_string(param, "driver", "default"); if (0 == strcmp(value, "default")) ad->driver = ao_default_driver_id(); else ad->driver = ao_driver_id(value); if (ad->driver < 0) { g_set_error(error, ao_output_quark(), 0, "\"%s\" is not a valid ao driver", value); ao_base_finish(&ad->base); g_free(ad); return NULL; } if ((ai = ao_driver_info(ad->driver)) == NULL) { g_set_error(error, ao_output_quark(), 0, "problems getting driver info"); ao_base_finish(&ad->base); g_free(ad); return NULL; } g_debug("using ao driver \"%s\" for \"%s\"\n", ai->short_name, config_get_block_string(param, "name", NULL)); value = config_get_block_string(param, "options", NULL); if (value != NULL) { gchar **options = g_strsplit(value, ";", 0); for (unsigned i = 0; options[i] != NULL; ++i) { gchar **key_value = g_strsplit(options[i], "=", 2); if (key_value[0] == NULL || key_value[1] == NULL) { g_set_error(error, ao_output_quark(), 0, "problems parsing options \"%s\"", options[i]); ao_base_finish(&ad->base); g_free(ad); return NULL; } ao_append_option(&ad->options, key_value[0], key_value[1]); g_strfreev(key_value); } g_strfreev(options); } return &ad->base; }
ao_device *open_ao_playdevice_buffer(struct mad_header const *header) { ao_sample_format format; ao_device *pldev = NULL; /* Because these can sometimes block, we stop our custom signal handler, and restore it afterwards */ signal(SIGINT, SIG_DFL); format.bits = 16; format.rate = header->samplerate; format.channels = (options.opt & MPG321_FORCE_STEREO) ? 2 : MAD_NCHANNELS(header); /* Add this element as an option to mpg321 */ format.matrix = "L,R"; /* mad gives us little-endian data; we swap it on big-endian targets, to big-endian format, because that's what most drivers expect. */ format.byte_format = AO_FMT_NATIVE; if(options.opt & MPG321_USE_AU) { int driver_id = ao_driver_id("au"); ao_option *ao_options = NULL; /* Don't have to check options.device here: we only define MPG321_USE_AU when --au <aufile> is defined, and <aufile> is pointd to by options.device */ if((pldev=ao_open_file(driver_id, options.device, 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening libao file output driver to write AU data.\n"); return NULL; } } else if (options.opt & MPG321_USE_CDR) { ao_option * ao_options = NULL; int driver_id = ao_driver_id("raw"); /* because CDR is a special format, i.e. headerless PCM, big endian, this is a special case. */ ao_append_option(&ao_options, "byteorder", "big"); if((pldev=ao_open_file(driver_id, options.device, 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening libao file output driver to write CDR data.\n"); return NULL; } } /* if the user specifies both au and wave, wav will be prefered, so testing * later */ else if(options.opt & MPG321_USE_WAV) { int driver_id = ao_driver_id("wav"); ao_option *ao_options = NULL; /* Don't have to check options.device here: we only define MPG321_USE_WAV when -w <wavfile> is defined, and <wavfile> is pointd to by options.device */ if((pldev=ao_open_file(driver_id, options.device, 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening libao wav file driver. (Do you have write permissions?)\n"); return NULL; } } else if(options.opt & MPG321_USE_NULL) { int driver_id = ao_driver_id("null"); /* null is dirty, create a proper options struct later */ if((pldev = ao_open_live(driver_id, &format, NULL)) == NULL) { fprintf(stderr, "Error opening libao null driver. (This shouldn't have happened.)\n"); return NULL; } } else if (options.opt & MPG321_USE_STDOUT) { ao_option * ao_options = NULL; int driver_id = ao_driver_id("raw"); /* stdout output is expected to be little-endian generally */ ao_append_option(&ao_options, "byteorder", "little"); if((pldev=ao_open_file(driver_id, "-", 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening libao raw output driver.\n"); return NULL; } } else if (options.opt & MPG321_USE_USERDEF) { ao_option *ao_options = NULL; int driver_id = ao_driver_id(options.devicetype); if (driver_id < 0) { fprintf(stderr, "Can't open unknown ao driver %s\n", options.devicetype); exit(1); } if (playdevice_is_live()) { if (options.device) { fprintf(stderr, "Can't set output device to %s for unknown ao plugin %s", options.device, options.devicetype); } if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { fprintf(stderr, "Error opening unknown libao %s driver. (Is device in use?)\n", options.devicetype); return NULL; } } else { if (options.device) { /* Just assume that options.device is a filename. The user can shoot themselves in the foot all they like... */ if((pldev=ao_open_file(driver_id, options.device, 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening unknown libao %s file driver for file %s. (Do you have write permissions?)\n", options.devicetype, options.device); return NULL; } } else { fprintf(stderr, "Filename must be specified (with -a filename) for unknown ao driver %s\n", options.devicetype); } } } else { /* Hack-tacular. This code tries to the device as specified; if it can't, it'll fall through to the other devices, trying each in turn. If the user specified a device to use, though, it won't fall through: principle of least surprise */ int opened = 0; if(!opened && options.opt & MPG321_USE_ALSA) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("alsa"); char *c; char *card=(char *)malloc((int)16); memset(card,0,16); strncat(card,"alsa:\0",6); if (options.device) { strcat(card,options.device); //if ((c = strchr(options.device, ':')) == NULL || strlen(c+1) < 1) if ((c = strchr(card, ':')) == NULL || strlen(c+1) < 1) { fprintf(stderr, "Poorly formed ALSA card:device specification %s", options.device); exit(1); } *(c++) = '\0'; /* change the : to a null to create two separate strings */ //ao_append_option(&ao_options, "card", options.device); ao_append_option(&ao_options, "card", card); ao_append_option(&ao_options, "dev", c); } if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_ALSA09; } else opened++; } if(!opened && options.opt & MPG321_USE_ALSA09) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("alsa09"); if(options.device) ao_append_option(&ao_options, "dev", options.device); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_OSS; } else opened++; } if(!opened && options.opt & MPG321_USE_OSS) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("oss"); if(options.device) ao_append_option(&ao_options, "dsp", options.device); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_SUN; } else opened++; } if(!opened && options.opt & MPG321_USE_SUN) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("sun"); if(options.device) ao_append_option(&ao_options, "dev", options.device); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_ESD; } } if(!opened && options.opt & MPG321_USE_ESD) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("esd"); if(options.device) ao_append_option(&ao_options, "host", options.device); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_ARTS; } else opened++; } if(!opened && options.opt & MPG321_USE_ARTS) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("arts"); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { fprintf(stderr, "Can't find a suitable libao driver. (Is device in use?)\n"); return NULL; } } } /* Restore signal handler */ signal(SIGINT, handle_signals); return pldev; }
static int init(int argc, char **argv) { const char *str; int value; ao_initialize(); int driver = ao_default_driver_id(); ao_option *ao_opts = NULL; config.audio_backend_buffer_desired_length = 44100; // one second. config.audio_backend_latency_offset = 0; // get settings from settings file first, allow them to be overridden by command line options if (config.cfg != NULL) { /* Get the desired buffer size setting. */ if (config_lookup_int(config.cfg, "ao.audio_backend_buffer_desired_length", &value)) { if ((value < 0) || (value > 66150)) die("Invalid a0 audio backend buffer desired length \"%d\". It should be between 0 and " "66150, default is 44100", value); else { config.audio_backend_buffer_desired_length = value; } } /* Get the latency offset. */ if (config_lookup_int(config.cfg, "ao.audio_backend_latency_offset", &value)) { if ((value < -66150) || (value > 66150)) die("Invalid ao audio backend buffer latency offset \"%d\". It should be between -66150 and +66150, default is 0", value); else config.audio_backend_latency_offset = value; } } optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour argv--; // so we shift the arguments to satisfy getopt() argc++; // some platforms apparently require optreset = 1; - which? int opt; char *mid; while ((opt = getopt(argc, argv, "d:i:n:o:")) > 0) { switch (opt) { case 'd': driver = ao_driver_id(optarg); if (driver < 0) die("could not find ao driver %s", optarg); break; case 'i': ao_append_option(&ao_opts, "id", optarg); break; case 'n': ao_append_option(&ao_opts, "dev", optarg); // Old libao versions (for example, 0.8.8) only support // "dsp" instead of "dev". ao_append_option(&ao_opts, "dsp", optarg); break; case 'o': mid = strchr(optarg, '='); if (!mid) die("Expected an = in audio option %s", optarg); *mid = 0; ao_append_option(&ao_opts, optarg, mid + 1); break; default: help(); die("Invalid audio option -%c specified", opt); } } if (optind < argc) die("Invalid audio argument: %s", argv[optind]); ao_sample_format fmt; memset(&fmt, 0, sizeof(fmt)); fmt.bits = 16; fmt.rate = 44100; fmt.channels = 2; fmt.byte_format = AO_FMT_NATIVE; dev = ao_open_live(driver, &fmt, ao_opts); return dev ? 0 : 1; }
int ao_append_global_option(const char *key, const char *value) { return ao_append_option(&ao_global_options,key,value); }
int parse_cmdline_options (int argc, char **argv, ogg123_options_t *ogg123_opts, file_option_t *file_opts) { int option_index = 1; ao_option *temp_options = NULL; ao_option ** current_options = &temp_options; ao_info *info; int temp_driver_id = -1; audio_device_t *current; int ret; while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:K:o:p:qrRvVx:y:zZ@:", long_options, &option_index))) { switch (ret) { case 0: if(!strcmp(long_options[option_index].name, "audio-buffer")) { ogg123_opts->buffer_size = 1024 * atoi(optarg); } else { status_error(_("Internal error parsing command line options.\n")); exit(1); } break; case 'b': ogg123_opts->input_buffer_size = atoi(optarg) * 1024; if (ogg123_opts->input_buffer_size < MIN_INPUT_BUFFER_SIZE * 1024) { status_error(_("Input buffer size smaller than minimum size of %dkB."), MIN_INPUT_BUFFER_SIZE); ogg123_opts->input_buffer_size = MIN_INPUT_BUFFER_SIZE * 1024; } break; case 'c': if (optarg) { char *tmp = strdup (optarg); parse_code_t pcode = parse_line(file_opts, tmp); if (pcode != parse_ok) status_error(_("=== Error \"%s\" while parsing config option from command line.\n" "=== Option was: %s\n"), parse_error_string(pcode), optarg); free (tmp); } else { /* not using the status interface here */ fprintf (stdout, _("Available options:\n")); file_options_describe(file_opts, stdout); exit (0); } break; case 'd': temp_driver_id = ao_driver_id(optarg); if (temp_driver_id < 0) { status_error(_("=== No such device %s.\n"), optarg); exit(1); } current = append_audio_device(ogg123_opts->devices, temp_driver_id, NULL, NULL); if(ogg123_opts->devices == NULL) ogg123_opts->devices = current; current_options = ¤t->options; break; case 'f': if (temp_driver_id >= 0) { info = ao_driver_info(temp_driver_id); if (info->type == AO_TYPE_FILE) { free(current->filename); current->filename = strdup(optarg); } else { status_error(_("=== Driver %s is not a file output driver.\n"), info->short_name); exit(1); } } else { status_error(_("=== Cannot specify output file without specifying a driver.\n")); exit (1); } break; case 'k': set_seek_opt(ogg123_opts, optarg); break; case 'K': ogg123_opts->endpos = strtotime(optarg); break; case 'l': ogg123_opts->delay = atoi(optarg); break; case 'o': if (optarg && !add_ao_option(current_options, optarg)) { status_error(_("=== Incorrect option format: %s.\n"), optarg); exit(1); } break; case 'h': cmdline_usage(); exit(0); break; case 'p': ogg123_opts->input_prebuffer = atof (optarg); if (ogg123_opts->input_prebuffer < 0.0f || ogg123_opts->input_prebuffer > 100.0f) { status_error (_("--- Prebuffer value invalid. Range is 0-100.\n")); ogg123_opts->input_prebuffer = ogg123_opts->input_prebuffer < 0.0f ? 0.0f : 100.0f; } break; case 'q': ogg123_opts->verbosity = 0; break; case 'r': ogg123_opts->repeat = 1; break; case 'R': ogg123_opts->remote = 1; ogg123_opts->verbosity = 0; break; case 'v': ogg123_opts->verbosity++; break; case 'V': status_error(_("ogg123 from %s %s"), PACKAGE, VERSION); exit(0); break; case 'x': ogg123_opts->nth = atoi(optarg); if (ogg123_opts->nth == 0) { status_error(_("--- Cannot play every 0th chunk!\n")); ogg123_opts->nth = 1; } break; case 'y': ogg123_opts->ntimes = atoi(optarg); if (ogg123_opts->ntimes == 0) { status_error(_("--- Cannot play every chunk 0 times.\n" "--- To do a test decode, use the null output driver.\n")); ogg123_opts->ntimes = 1; } break; case 'z': ogg123_opts->shuffle = 1; break; case 'Z': ogg123_opts->repeat = ogg123_opts->shuffle = 1; break; case '@': if (playlist_append_from_file(ogg123_opts->playlist, optarg) == 0) status_error(_("--- Cannot open playlist file %s. Skipped.\n"), optarg); break; case '?': break; default: cmdline_usage(); exit(1); } } /* Sanity check bad option combinations */ if (ogg123_opts->endpos > 0.0 && ogg123_opts->seekoff > ogg123_opts->endpos) { status_error(_("=== Option conflict: End time is before start time.\n")); exit(1); } /* Add last device to device list or use the default device */ if (temp_driver_id < 0) { /* First try config file setting */ if (ogg123_opts->default_device) { temp_driver_id = ao_driver_id(ogg123_opts->default_device); if (temp_driver_id < 0) status_error(_("--- Driver %s specified in configuration file invalid.\n"), ogg123_opts->default_device); } /* Then try libao autodetect */ if (temp_driver_id < 0) temp_driver_id = ao_default_driver_id(); /* Finally, give up */ if (temp_driver_id < 0) { status_error(_("=== Could not load default driver and no driver specified in config file. Exiting.\n")); exit(1); } ogg123_opts->devices = append_audio_device(ogg123_opts->devices, temp_driver_id, temp_options, NULL); } /* if verbosity has been altered, add options to drivers... */ { audio_device_t *head = ogg123_opts->devices; while (head){ if(ogg123_opts->verbosity>3) ao_append_global_option("debug",NULL); if(ogg123_opts->verbosity>2) ao_append_option(&(head->options),"verbose",NULL); if(ogg123_opts->verbosity==0) ao_append_option(&(head->options),"quiet",NULL); head = head->next_device; } } return optind; }