]> git.ipfire.org Git - thirdparty/shairport-sync.git/blame - audio_pw.c
Send silence when no audio is coming through from Shairport Sync. This eliminates...
[thirdparty/shairport-sync.git] / audio_pw.c
CommitLineData
ec232363 1/*
afaf94b7 2 * Asynchronous PipeWire Backend. This file is part of Shairport Sync.
bde2bcc8 3 * Copyright (c) Mike Brady 2023
ec232363
LR
4 * All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 */
26
bde2bcc8
MB
27// This uses ideas from the tone generator sample code at:
28// https://github.com/PipeWire/pipewire/blob/master/src/examples/audio-src.c
dc197ae7 29// Thanks to Wim Taymans.
bde2bcc8 30
ec232363
LR
31#include "audio.h"
32#include "common.h"
afaf94b7
MB
33#include <errno.h>
34#include <pthread.h>
35#include <stdio.h>
36#include <string.h>
37#include <unistd.h>
ec232363 38
afaf94b7 39#include <pipewire/pipewire.h>
27e4ba09 40#include <spa/param/audio/format-utils.h>
ec232363 41
dc197ae7 42// note -- these are hardwired into this code.
afaf94b7
MB
43#define DEFAULT_FORMAT SPA_AUDIO_FORMAT_S16_LE
44#define DEFAULT_RATE 44100
45#define DEFAULT_CHANNELS 2
ec232363 46
afaf94b7
MB
47// Four seconds buffer -- should be plenty
48#define buffer_allocation 44100 * 4 * 2 * 2
ec232363 49
27e4ba09 50static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
ec232363 51
27e4ba09
MB
52static char *audio_lmb, *audio_umb, *audio_toq, *audio_eoq;
53static size_t audio_size = buffer_allocation;
54static size_t audio_occupancy;
dc197ae7 55static int enable_fill;
ec232363 56
27e4ba09 57struct timing_data {
bde2bcc8 58 int pw_time_is_valid; // set when the pw_time has been set
27e4ba09 59 struct pw_time time_info; // information about the last time a process callback occurred
bde2bcc8 60 size_t frames; // the number of frames sent at that time
27e4ba09
MB
61};
62
63// to avoid using a mutex, write the same data twice and check they are the same
64// to ensure they are consistent. Make sure the first is written strictly before the second
65// using __sync_synchronize();
66struct timing_data timing_data_1, timing_data_2;
67
afaf94b7
MB
68struct data {
69 struct pw_thread_loop *loop;
70 struct pw_stream *stream;
ec232363
LR
71};
72
afaf94b7 73// the pipewire global data structure
dc197ae7 74struct data data = {NULL, NULL};
ec232363 75
dc197ae7 76/*
bde2bcc8
MB
77static void on_state_changed(__attribute__((unused)) void *userdata, enum pw_stream_state old,
78 enum pw_stream_state state,
79 __attribute__((unused)) const char *error) {
80 // struct pw_data *pw = userdata;
81 debug(3, "pw: stream state changed %s -> %s", pw_stream_state_as_string(old),
82 pw_stream_state_as_string(state));
83}
dc197ae7 84*/
bde2bcc8
MB
85
86static void on_process(void *userdata) {
87
88 struct data *data = userdata;
89 int n_frames = 0;
90
27e4ba09 91 pthread_mutex_lock(&buffer_mutex);
bde2bcc8 92
dc197ae7 93 if ((audio_occupancy > 0) || (enable_fill)) {
bde2bcc8
MB
94
95 // get a buffer to see how big it can be
96 struct pw_buffer *b = pw_stream_dequeue_buffer(data->stream);
97 if (b == NULL) {
98 pw_log_warn("out of buffers: %m");
dc197ae7 99 die("PipeWire failue -- out of buffers!");
bde2bcc8
MB
100 }
101 struct spa_buffer *buf = b->buffer;
102 uint8_t *dest = buf->datas[0].data;
dc197ae7
MB
103 if (dest != NULL) {
104 int stride = sizeof(int16_t) * DEFAULT_CHANNELS;
105
106 // note: the requested field is the number of frames, not bytes, requested
107 int max_possible_frames = SPA_MIN(b->requested, buf->datas[0].maxsize / stride);
108
109 size_t bytes_we_can_transfer = max_possible_frames * stride;
110
111 if (audio_occupancy > 0) {
112 // if (enable_fill == 1)) {
113 // debug(1, "got audio -- disable_fill");
114 // }
115 enable_fill = 0;
116
117 if (bytes_we_can_transfer > audio_occupancy)
118 bytes_we_can_transfer = audio_occupancy;
119
120 n_frames = bytes_we_can_transfer / stride;
121
122 size_t bytes_to_end_of_buffer = (size_t)(audio_umb - audio_toq); // must be zero or positive
123 if (bytes_we_can_transfer <= bytes_to_end_of_buffer) {
124 // the bytes are all in a row in the audio buffer
125 memcpy(dest, audio_toq, bytes_we_can_transfer);
126 audio_toq += bytes_we_can_transfer;
127 } else {
128 // the bytes are in two places in the audio buffer
129 size_t first_portion_to_write = audio_umb - audio_toq;
130 if (first_portion_to_write != 0)
131 memcpy(dest, audio_toq, first_portion_to_write);
132 uint8_t *new_dest = dest + first_portion_to_write;
133 memcpy(new_dest, audio_lmb, bytes_we_can_transfer - first_portion_to_write);
134 audio_toq = audio_lmb + bytes_we_can_transfer - first_portion_to_write;
135 }
136 audio_occupancy -= bytes_we_can_transfer;
137
138 } else {
139 debug(3, "send silence");
140 // this should really be dithered silence
141 memset(dest, 0, bytes_we_can_transfer);
142 n_frames = max_possible_frames;
143 }
144 buf->datas[0].chunk->offset = 0;
145 buf->datas[0].chunk->stride = stride;
146 buf->datas[0].chunk->size = n_frames * stride;
147 pw_stream_queue_buffer(data->stream, b);
148 debug(3, "Queueing %d frames for output.", n_frames);
149 } // (else the first data block does not contain a data pointer)
bde2bcc8
MB
150 }
151 pthread_mutex_unlock(&buffer_mutex);
ec232363 152
bde2bcc8
MB
153 timing_data_1.frames = n_frames;
154 if (pw_stream_get_time_n(data->stream, &timing_data_1.time_info, sizeof(struct timing_data)) == 0)
27e4ba09
MB
155 timing_data_1.pw_time_is_valid = 1;
156 else
157 timing_data_1.pw_time_is_valid = 0;
158 __sync_synchronize();
159 memcpy((char *)&timing_data_2, (char *)&timing_data_1, sizeof(struct timing_data));
160 __sync_synchronize();
ec232363
LR
161}
162
dc197ae7
MB
163static const struct pw_stream_events stream_events = {PW_VERSION_STREAM_EVENTS,
164 .process = on_process};
165// PW_VERSION_STREAM_EVENTS, .process = on_process, .state_changed = on_state_changed};
bde2bcc8
MB
166
167static void deinit(void) {
168 pw_thread_loop_stop(data.loop);
169 pw_stream_destroy(data.stream);
170 pw_thread_loop_destroy(data.loop);
171 pw_deinit();
172 free(audio_lmb); // deallocate that buffer
173}
ec232363 174
c0a3dacf 175static int init(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) {
ec232363 176 // set up default values first
bde2bcc8
MB
177 memset(&timing_data_1, 0, sizeof(struct timing_data));
178 memset(&timing_data_2, 0, sizeof(struct timing_data));
ec232363 179 config.audio_backend_buffer_desired_length = 0.35;
afaf94b7
MB
180 config.audio_backend_buffer_interpolation_threshold_in_seconds =
181 0.02; // below this, soxr interpolation will not occur -- it'll be basic interpolation
182 // instead.
ec232363 183
afaf94b7 184 config.audio_backend_latency_offset = 0;
ec232363 185
afaf94b7 186 // get settings from settings file
afaf94b7
MB
187 // do the "general" audio options. Note, these options are in the "general" stanza!
188 parse_general_audio_options();
ec232363 189
27e4ba09
MB
190 /*
191 // now any PipeWire-specific options
192 if (config.cfg != NULL) {
193 const char *str;
194 }
195 */
afaf94b7 196 // finished collecting settings
ec232363 197
afaf94b7
MB
198 // allocate space for the audio buffer
199 audio_lmb = malloc(audio_size);
200 if (audio_lmb == NULL)
dc197ae7 201 die("Can't allocate %d bytes for PipeWire buffer.", audio_size);
afaf94b7
MB
202 audio_toq = audio_eoq = audio_lmb;
203 audio_umb = audio_lmb + audio_size;
204 audio_occupancy = 0;
dc197ae7
MB
205 // debug(1, "init enable_fill");
206 enable_fill = 1;
ec232363 207
afaf94b7
MB
208 const struct spa_pod *params[1];
209 uint8_t buffer[1024];
210 struct pw_properties *props;
211 struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
212
213 int largc = 0;
214 pw_init(&largc, NULL);
215
bde2bcc8
MB
216 /* make a threaded loop. */
217 data.loop = pw_thread_loop_new("shairport-sync", NULL);
afaf94b7
MB
218
219 pw_thread_loop_lock(data.loop);
27e4ba09 220
afaf94b7
MB
221 pw_thread_loop_start(data.loop);
222
afaf94b7 223 props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Playback",
afdf0d86
MB
224 PW_KEY_MEDIA_ROLE, "Music", PW_KEY_APP_NAME, "Shairport Sync", NULL);
225
bde2bcc8 226 data.stream = pw_stream_new_simple(pw_thread_loop_get_loop(data.loop), "shairport-sync", props,
afaf94b7
MB
227 &stream_events, &data);
228
229 /* Make one parameter with the supported formats. The SPA_PARAM_EnumFormat
230 * id means that this is a format enumeration (of 1 value). */
231 params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
232 &SPA_AUDIO_INFO_RAW_INIT(.format = SPA_AUDIO_FORMAT_S16_LE,
233 .channels = DEFAULT_CHANNELS,
234 .rate = DEFAULT_RATE));
235
236 /* Now connect this stream. We ask that our process function is
237 * called in a realtime thread. */
238 pw_stream_connect(data.stream, PW_DIRECTION_OUTPUT, PW_ID_ANY,
239 PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS |
240 PW_STREAM_FLAG_RT_PROCESS,
241 params, 1);
242
243 pw_thread_loop_unlock(data.loop);
ec232363
LR
244 return 0;
245}
246
27e4ba09 247static void start(__attribute__((unused)) int sample_rate,
dc197ae7
MB
248 __attribute__((unused)) int sample_format) {
249}
27e4ba09
MB
250
251static int play(__attribute__((unused)) void *buf, int samples,
252 __attribute__((unused)) int sample_type, __attribute__((unused)) uint32_t timestamp,
bde2bcc8 253 __attribute__((unused)) uint64_t playtime) {
afaf94b7 254 // copy the samples into the queue
bde2bcc8 255 debug(3, "play %u samples; %u bytes already in the buffer.", samples, audio_occupancy);
afaf94b7 256 size_t bytes_to_transfer = samples * 2 * 2;
bde2bcc8
MB
257 pthread_mutex_lock(&buffer_mutex);
258 size_t bytes_available = audio_size - audio_occupancy;
259 if (bytes_available < bytes_to_transfer)
260 bytes_to_transfer = bytes_available;
261 if (bytes_to_transfer > 0) {
262 size_t space_to_end_of_buffer = audio_umb - audio_eoq;
263 if (space_to_end_of_buffer >= bytes_to_transfer) {
264 memcpy(audio_eoq, buf, bytes_to_transfer);
265 audio_eoq += bytes_to_transfer;
266 } else {
267 memcpy(audio_eoq, buf, space_to_end_of_buffer);
268 buf += space_to_end_of_buffer;
269 memcpy(audio_lmb, buf, bytes_to_transfer - space_to_end_of_buffer);
270 audio_eoq = audio_lmb + bytes_to_transfer - space_to_end_of_buffer;
271 }
afaf94b7 272 audio_occupancy += bytes_to_transfer;
ec232363 273 }
bde2bcc8 274 pthread_mutex_unlock(&buffer_mutex);
afaf94b7 275 return 0;
ec232363
LR
276}
277
27e4ba09 278int delay(long *the_delay) {
bde2bcc8
MB
279 long result = 0;
280 int reply = 0;
27e4ba09
MB
281 // find out what's already in the PipeWire system and when
282 struct timing_data timing_data;
283 int loop_count = 1;
284 do {
285 memcpy(&timing_data, (char *)&timing_data_1, sizeof(struct timing_data));
286 __sync_synchronize();
287 if (memcmp(&timing_data, (char *)&timing_data_2, sizeof(struct timing_data)) != 0) {
bde2bcc8
MB
288 usleep(2); // microseconds
289 loop_count++;
290 __sync_synchronize();
27e4ba09 291 }
bde2bcc8
MB
292 } while ((memcmp(&timing_data, (char *)&timing_data_2, sizeof(struct timing_data)) != 0) &&
293 (loop_count < 10));
27e4ba09
MB
294 long total_delay_now_frames_long = 0;
295 if ((loop_count < 10) && (timing_data.pw_time_is_valid != 0)) {
bde2bcc8 296 struct timespec time_now;
27e4ba09 297 clock_gettime(CLOCK_MONOTONIC, &time_now);
bde2bcc8
MB
298 int64_t interval_from_process_time_to_now =
299 SPA_TIMESPEC_TO_NSEC(&time_now) - timing_data.time_info.now;
27e4ba09
MB
300 int64_t delay_in_ns = timing_data.time_info.delay + timing_data.time_info.buffered;
301 delay_in_ns = delay_in_ns * 1000000000;
302 delay_in_ns = delay_in_ns * timing_data.time_info.rate.num;
303 delay_in_ns = delay_in_ns / timing_data.time_info.rate.denom;
bde2bcc8 304
27e4ba09 305 int64_t total_delay_now_ns = delay_in_ns - interval_from_process_time_to_now;
bde2bcc8 306 int64_t total_delay_now_frames = (total_delay_now_ns * 44100) / 1000000000 + timing_data.frames;
27e4ba09 307 total_delay_now_frames_long = total_delay_now_frames;
bde2bcc8 308 debug(3, "total delay in frames: %ld.", total_delay_now_frames_long);
27e4ba09 309
bde2bcc8
MB
310 if (timing_data.time_info.queued != 0) {
311 debug(1, "buffers queued: %d", timing_data.time_info.queued);
312 }
313 /*
314 debug(3,
315 "interval_from_process_time_to_now: %" PRId64 " ns, "
316 "delay_in_ns: %" PRId64 ", queued: %" PRId64 ", buffered: %" PRId64 ".",
317 // delay_timing_data.time_info.rate.num, delay_timing_data.time_info.rate.denom,
318 interval_from_process_time_to_now, delay_in_ns,
319 timing_data.time_info.queued, timing_data.time_info.buffered);
320 */
27e4ba09 321
afaf94b7 322 } else {
27e4ba09 323 debug(1, "can't get time info.");
ec232363 324 }
27e4ba09 325
27e4ba09
MB
326 pthread_mutex_lock(&buffer_mutex);
327 result = total_delay_now_frames_long + audio_occupancy / (2 * 2);
328 pthread_mutex_unlock(&buffer_mutex);
afaf94b7
MB
329 *the_delay = result;
330 return reply;
ec232363
LR
331}
332
27e4ba09 333static void flush(void) {
bde2bcc8 334 pthread_mutex_lock(&buffer_mutex);
afaf94b7
MB
335 audio_toq = audio_eoq = audio_lmb;
336 audio_umb = audio_lmb + audio_size;
337 audio_occupancy = 0;
dc197ae7
MB
338 // if (enable_fill == 0) {
339 // debug(1, "flush enable_fill");
340 // }
341 enable_fill = 1;
27e4ba09 342 pthread_mutex_unlock(&buffer_mutex);
ec232363
LR
343}
344
afaf94b7 345static void stop(void) {
bde2bcc8 346 pthread_mutex_lock(&buffer_mutex);
afaf94b7
MB
347 audio_toq = audio_eoq = audio_lmb;
348 audio_umb = audio_lmb + audio_size;
349 audio_occupancy = 0;
dc197ae7
MB
350 // if (enable_fill == 0) {
351 // debug(1, "stop enable_fill");
352 // }
353 enable_fill = 1;
27e4ba09 354 pthread_mutex_unlock(&buffer_mutex);
ec232363
LR
355}
356
c0a3dacf
MB
357audio_output audio_pw = {.name = "pw",
358 .help = NULL,
359 .init = &init,
360 .deinit = &deinit,
361 .prepare = NULL,
27e4ba09 362 .start = &start,
c0a3dacf
MB
363 .stop = &stop,
364 .is_running = NULL,
365 .flush = &flush,
27e4ba09 366 .delay = &delay,
cd9da86f 367 .stats = NULL,
c0a3dacf
MB
368 .play = &play,
369 .volume = NULL,
370 .parameters = NULL,
afaf94b7 371 .mute = NULL};