Skip to content

Commit

Permalink
Close the driver when play stops; reopen it when it restarts. Seems a…
Browse files Browse the repository at this point in the history
… lot more stable. Add a list of drivers to the help text.
  • Loading branch information
mikebrady committed Aug 9, 2022
1 parent 36dbd79 commit cb03d5c
Showing 1 changed file with 111 additions and 67 deletions.
178 changes: 111 additions & 67 deletions audio_ao.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* libao output driver. This file is part of Shairport.
* Copyright (c) James Laird 2013
* Copyright (c) Mike Brady 2014 -- 2022
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
Expand Down Expand Up @@ -32,98 +33,141 @@
#include <unistd.h>

ao_device *dev = NULL;
ao_option *ao_opts = NULL;
ao_sample_format fmt;
int driver = 0;

static void help(void) {
printf(" -d driver set the output driver\n"
" -o name=value set an arbitrary ao option\n"
" -i id shorthand for -o id=<id>\n"
" -n name shorthand for -o dev=<name> -o dsp=<name>\n");
// get a list of drivers available
ao_initialize();
int defaultDriver = ao_default_driver_id();
if (defaultDriver == -1) {
printf(" No usable drivers available.\n");
} else {
ao_info *defaultDriverInfo = ao_driver_info(defaultDriver);
int driver_count;
ao_info **driver_list = ao_driver_info_list(&driver_count);
int i = 0;
if (driver_count == 0) {
printf(" Driver list unavailable.\n");
} else {
printf(" Drivers:\n");
for (i = 0; i < driver_count; i++) {
ao_info *the_driver = driver_list[i];
if (strcmp(the_driver->short_name, defaultDriverInfo->short_name) == 0)
printf(" \"%s\" (default)\n", the_driver->short_name);
else
printf(" \"%s\"\n", the_driver->short_name);
}
}
}
ao_shutdown();
}

static int init(int argc, char **argv) {
// const char *str;
// int value;
// double dvalue;
ao_initialize();
int driver = ao_default_driver_id();
ao_option *ao_opts = NULL;

// set up default values first

config.audio_backend_buffer_desired_length = 1.0;
config.audio_backend_latency_offset = 0;

// get settings from settings file first, allow them to be overridden by
// command line options

// do the "general" audio options. Note, these options are in the "general" stanza!
parse_general_audio_options();

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);
driver = ao_default_driver_id();
if (driver == -1) {
warn("libao can not find a usable driver!");
} else {

// set up default values first

config.audio_backend_buffer_desired_length = 1.0;
config.audio_backend_latency_offset = 0;

// get settings from settings file first, allow them to be overridden by
// command line options

// do the "general" audio options. Note, these options are in the "general" stanza!
parse_general_audio_options();

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;
if (optind < argc)
die("Invalid audio argument: %s", argv[optind]);

dev = ao_open_live(driver, &fmt, ao_opts);
memset(&fmt, 0, sizeof(fmt));

return dev ? 0 : 1;
fmt.bits = 16;
fmt.rate = 44100;
fmt.channels = 2;
fmt.byte_format = AO_FMT_NATIVE;
}
return 0;
}

static void deinit(void) {
if (dev)
if (dev != NULL)
ao_close(dev);
dev = NULL;
ao_shutdown();
}

static void start(__attribute__((unused)) int sample_rate,
__attribute__((unused)) int sample_format) {}
__attribute__((unused)) int sample_format) {
// debug(1,"libao start");
}

static int play(void *buf, int samples) { return ao_play(dev, buf, samples * 4); }
static int play(void *buf, int samples) {
int response = 0;
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
if (driver != -1) {
if (dev == NULL)
dev = ao_open_live(driver, &fmt, ao_opts);
if (dev != NULL)
response = ao_play(dev, buf, samples * 4);
}
pthread_setcancelstate(oldState, NULL);
return response;
}

static void stop(void) {}
static void stop(void) {
// debug(1,"libao stop");
if (dev != NULL) {
ao_close(dev);
dev = NULL;
}
}

audio_output audio_ao = {.name = "ao",
.help = &help,
Expand Down

0 comments on commit cb03d5c

Please sign in to comment.