]> git.ipfire.org Git - thirdparty/shairport-sync.git/blame - audio_sndio.c
Update check_classic_mac_basic.yml
[thirdparty/shairport-sync.git] / audio_sndio.c
CommitLineData
629e202f 1/*
c677ad48 2 * sndio output driver. This file is part of Shairport Sync.
629e202f 3 * Copyright (c) 2013 Dimitri Sokolyuk <demon@dim13.org>
da12c449 4 * Copyright (c) 2017 Tobias Kortkamp <t@tobik.me>
629e202f 5 *
107df039 6 * Modifications for audio synchronisation
55d5d496 7 * and related work, copyright (c) Mike Brady 2014 -- 2022
107df039
MB
8 * All rights reserved.
9 *
629e202f
DS
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
064bd293 23#include "audio.h"
da12c449
TK
24#include "common.h"
25#include <pthread.h>
064bd293 26#include <sndio.h>
629e202f 27#include <stdio.h>
da12c449 28#include <string.h>
629e202f 29#include <unistd.h>
629e202f 30
da12c449
TK
31static pthread_mutex_t sndio_mutex = PTHREAD_MUTEX_INITIALIZER;
32static struct sio_hdl *hdl;
33static int framesize;
34static size_t played;
35static size_t written;
55d5d496 36uint64_t time_of_last_onmove_cb;
4d169d7b 37int at_least_one_onmove_cb_seen;
55d5d496 38
b7864b4e 39struct sio_par par;
da12c449
TK
40
41struct sndio_formats {
42 const char *name;
85ba78b6 43 sps_format_t fmt;
dc393b98 44 unsigned int rate;
da12c449
TK
45 unsigned int bits;
46 unsigned int bps;
47 unsigned int sig;
48 unsigned int le;
49};
50
03eb21a9
MB
51static struct sndio_formats formats[] = {{"S8", SPS_FORMAT_S8, 44100, 8, 1, 1, SIO_LE_NATIVE},
52 {"U8", SPS_FORMAT_U8, 44100, 8, 1, 0, SIO_LE_NATIVE},
53 {"S16", SPS_FORMAT_S16, 44100, 16, 2, 1, SIO_LE_NATIVE},
c8b0be30
MB
54 {"AUTOMATIC", SPS_FORMAT_S16, 44100, 16, 2, 1,
55 SIO_LE_NATIVE}, // TODO: make this really automatic?
03eb21a9
MB
56 {"S24", SPS_FORMAT_S24, 44100, 24, 4, 1, SIO_LE_NATIVE},
57 {"S24_3LE", SPS_FORMAT_S24_3LE, 44100, 24, 3, 1, 1},
58 {"S24_3BE", SPS_FORMAT_S24_3BE, 44100, 24, 3, 1, 0},
55d5d496 59 {"S32", SPS_FORMAT_S32, 44100, 32, 4, 1, SIO_LE_NATIVE}};
da12c449 60
e513e533 61static void help() { printf(" -d output-device set the output device [default*|...]\n"); }
629e202f 62
fd880056
MB
63void onmove_cb(__attribute__((unused)) void *arg, int delta) {
64 time_of_last_onmove_cb = get_absolute_time_in_ns();
65 at_least_one_onmove_cb_seen = 1;
66 played += delta;
55d5d496
MB
67}
68
54250f75 69static int init(int argc, char **argv) {
0cd8a2ce
MB
70 int found, opt, round, rate, bufsz;
71 unsigned int i;
da12c449 72 const char *devname, *tmp;
629e202f 73
b7864b4e 74 // set up default values first
629e202f 75
87a0475c 76 sio_initpar(&par);
87a0475c
MB
77 par.rate = 44100;
78 par.pchan = 2;
da12c449
TK
79 par.bits = 16;
80 par.bps = SIO_BPS(par.bits);
81 par.le = 1;
87a0475c 82 par.sig = 1;
67aed3a3 83 devname = SIO_DEVANY;
e513e533 84
b7864b4e 85 config.audio_backend_buffer_desired_length = 1.0;
80f15e1f
MB
86 config.audio_backend_buffer_interpolation_threshold_in_seconds =
87 0.25; // below this, soxr interpolation will not occur -- it'll be basic interpolation
88 // instead.
b7864b4e 89 config.audio_backend_latency_offset = 0;
629e202f 90
e513e533
MB
91 // get settings from settings file
92
b7864b4e
MB
93 // do the "general" audio options. Note, these options are in the "general" stanza!
94 parse_general_audio_options();
95
96 // get the specific settings
e513e533 97
67aed3a3
MB
98 if (config.cfg != NULL) {
99 if (!config_lookup_string(config.cfg, "sndio.device", &devname))
100 devname = SIO_DEVANY;
101 if (config_lookup_int(config.cfg, "sndio.rate", &rate)) {
102 if (rate % 44100 == 0 && rate >= 44100 && rate <= 352800) {
fd880056 103 par.rate = rate;
67aed3a3
MB
104 } else {
105 die("sndio: output rate must be a multiple of 44100 and 44100 <= rate <= "
106 "352800");
107 }
da12c449 108 }
67aed3a3
MB
109 if (config_lookup_int(config.cfg, "sndio.bufsz", &bufsz)) {
110 if (bufsz > 0) {
111 par.appbufsz = bufsz;
112 } else {
113 die("sndio: bufsz must be > 0");
114 }
da12c449 115 }
67aed3a3
MB
116 if (config_lookup_int(config.cfg, "sndio.round", &round)) {
117 if (round > 0) {
118 par.round = round;
119 } else {
120 die("sndio: round must be > 0");
121 }
da12c449 122 }
67aed3a3
MB
123 if (config_lookup_string(config.cfg, "sndio.format", &tmp)) {
124 for (i = 0, found = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
3a180706 125 if (strcasecmp(formats[i].name, tmp) == 0) {
67aed3a3
MB
126 config.output_format = formats[i].fmt;
127 found = 1;
128 break;
129 }
da12c449 130 }
67aed3a3 131 if (!found)
3a180706 132 die("Invalid output format \"%s\". Should be one of: S8, U8, S16, S24, "
20967812 133 "S24_3LE, S24_3BE, S32, Automatic",
67aed3a3 134 tmp);
da12c449 135 }
da12c449 136 }
da12c449
TK
137 optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
138 argv--; // so we shift the arguments to satisfy getopt()
139 argc++;
140 while ((opt = getopt(argc, argv, "d:")) > 0) {
141 switch (opt) {
142 case 'd':
143 devname = optarg;
144 break;
145 default:
146 help();
147 die("Invalid audio option -%c specified", opt);
148 }
149 }
150 if (optind < argc)
151 die("Invalid audio argument: %s", argv[optind]);
55d5d496
MB
152 pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
153 // pthread_mutex_lock(&sndio_mutex);
154 debug(1, "sndio: output device name is \"%s\".", devname);
fd880056
MB
155 debug(1, "sndio: rate: %u.", par.rate);
156 debug(1, "sndio: bits: %u.", par.bits);
55d5d496 157
da12c449
TK
158 hdl = sio_open(devname, SIO_PLAY, 0);
159 if (!hdl)
160 die("sndio: cannot open audio device");
161
162 written = played = 0;
fd880056 163 time_of_last_onmove_cb = 0;
4d169d7b 164 at_least_one_onmove_cb_seen = 0;
da12c449
TK
165
166 for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
167 if (formats[i].fmt == config.output_format) {
168 par.bits = formats[i].bits;
169 par.bps = formats[i].bps;
170 par.sig = formats[i].sig;
171 par.le = formats[i].le;
172 break;
173 }
174 }
c8b0be30 175
da12c449 176 if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par))
87a0475c 177 die("sndio: failed to set audio parameters");
da12c449 178 for (i = 0, found = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
e513e533 179 if (formats[i].bits == par.bits && formats[i].bps == par.bps && formats[i].sig == par.sig &&
03eb21a9 180 formats[i].le == par.le && formats[i].rate == par.rate) {
da12c449
TK
181 config.output_format = formats[i].fmt;
182 found = 1;
183 break;
184 }
185 }
186 if (!found)
1b47023c 187 die("sndio: could not set output device to the required format and rate.");
87a0475c 188
da12c449
TK
189 framesize = par.bps * par.pchan;
190 config.output_rate = par.rate;
14bfba27
MB
191 if (par.rate == 0) {
192 die("sndio: par.rate set to zero.");
193 }
194
da12c449 195 config.audio_backend_buffer_desired_length = 1.0 * par.bufsz / par.rate;
54250f75 196 config.audio_backend_latency_offset = 0;
629e202f 197
da12c449
TK
198 sio_onmove(hdl, onmove_cb, NULL);
199
55d5d496
MB
200 // pthread_mutex_unlock(&sndio_mutex);
201 pthread_cleanup_pop(1); // unlock the mutex
14bfba27
MB
202 if (framesize == 0) {
203 die("sndio: framesize set to zero.");
204 }
87a0475c 205 return 0;
629e202f
DS
206}
207
da12c449 208static void deinit() {
55d5d496
MB
209 // pthread_mutex_lock(&sndio_mutex);
210 pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
da12c449 211 sio_close(hdl);
55d5d496
MB
212 // pthread_mutex_unlock(&sndio_mutex);
213 pthread_cleanup_pop(1); // unlock the mutex
da12c449 214}
629e202f 215
0cd8a2ce
MB
216static void start(__attribute__((unused)) int sample_rate,
217 __attribute__((unused)) int sample_format) {
55d5d496
MB
218 // pthread_mutex_lock(&sndio_mutex);
219 pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
55d5d496
MB
220 at_least_one_onmove_cb_seen = 0;
221 // any previously-reported frame count
222
da12c449
TK
223 if (!sio_start(hdl))
224 die("sndio: unable to start");
225 written = played = 0;
4d169d7b
MB
226 time_of_last_onmove_cb = 0;
227 at_least_one_onmove_cb_seen = 0;
55d5d496
MB
228 // pthread_mutex_unlock(&sndio_mutex);
229 pthread_cleanup_pop(1); // unlock the mutex
629e202f
DS
230}
231
fd880056
MB
232static int play(void *buf, int frames, __attribute__((unused)) int sample_type,
233 __attribute__((unused)) uint32_t timestamp,
234 __attribute__((unused)) uint64_t playtime) {
da12c449 235 if (frames > 0) {
55d5d496
MB
236 // pthread_mutex_lock(&sndio_mutex);
237 pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
da12c449 238 written += sio_write(hdl, buf, frames * framesize);
55d5d496
MB
239 // pthread_mutex_unlock(&sndio_mutex);
240 pthread_cleanup_pop(1); // unlock the mutex
da12c449 241 }
ea20840d 242 return 0;
629e202f
DS
243}
244
da12c449 245static void stop() {
55d5d496 246 pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
57af5f3c 247
da12c449
TK
248 if (!sio_stop(hdl))
249 die("sndio: unable to stop");
250 written = played = 0;
55d5d496 251 pthread_cleanup_pop(1); // unlock the mutex
da12c449 252}
629e202f 253
55d5d496
MB
254int get_delay(long *delay) {
255 int response = 0;
4d169d7b
MB
256 size_t estimated_extra_frames_output = 0;
257 if (at_least_one_onmove_cb_seen) { // when output starts, the onmove_cb callback will be made
b9cf91b2 258 // calculate the difference in time between now and when the last callback occurred,
4d169d7b 259 // and use it to estimate the frames that would have been output
fd880056 260 uint64_t time_difference = get_absolute_time_in_ns() - time_of_last_onmove_cb;
c2655b03 261 uint64_t frame_difference = (time_difference * par.rate) / 1000000000;
54d761ff 262 estimated_extra_frames_output = frame_difference;
c2655b03 263 // sanity check -- total estimate can not exceed frames written.
54d761ff 264 if ((estimated_extra_frames_output + played) > written / framesize) {
c2655b03
MB
265 // debug(1,"play estimate fails sanity check, possibly due to running on a VM");
266 estimated_extra_frames_output = 0; // can't make any sensible guess
54d761ff 267 }
e513e533
MB
268 // debug(1,"Frames played to last cb: %d, estimated to current time:
269 // %d.",played,estimated_extra_frames_output);
4d169d7b 270 }
432b56c6 271 if (delay != NULL)
55d5d496 272 *delay = (written / framesize) - (played + estimated_extra_frames_output);
55d5d496
MB
273 return response;
274}
275
276static int delay(long *delay) {
277 int result = 0;
278 pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
279 result = get_delay(delay);
280 pthread_cleanup_pop(1); // unlock the mutex
281 return result;
629e202f
DS
282}
283
da12c449 284static void flush() {
55d5d496
MB
285 // pthread_mutex_lock(&sndio_mutex);
286 pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
da12c449
TK
287 if (!sio_stop(hdl) || !sio_start(hdl))
288 die("sndio: unable to flush");
289 written = played = 0;
55d5d496
MB
290 // pthread_mutex_unlock(&sndio_mutex);
291 pthread_cleanup_pop(1); // unlock the mutex
292}
293
fd880056
MB
294audio_output audio_sndio = {.name = "sndio",
295 .help = &help,
296 .init = &init,
297 .deinit = &deinit,
298 .prepare = NULL,
299 .start = &start,
300 .stop = &stop,
301 .is_running = NULL,
302 .flush = &flush,
303 .delay = &delay,
304 .stats = NULL,
305 .play = &play,
306 .volume = NULL,
307 .parameters = NULL,
308 .mute = NULL};