chiark / gitweb /
Update word break algorithm for Unicode 5.1.0 (based on UAX #29).
[disorder] / lib / uaudio-schedule.c
CommitLineData
ec57f6c9
RK
1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2009 Richard Kettlewell
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18/** @file lib/uaudio-schedule.c
19 * @brief Scheduler for RTP and command backends
20 *
21 * These functions ensure that audio is only written at approximately the rate
22 * it should play at, allowing pause to function properly.
23 *
24 * OSS and ALSA we expect to be essentially synchronous (though we could use
25 * this code if they don't play nicely). Core Audio sorts out its own timing
26 * issues itself.
27 *
28 * The sequence numbers are intended for RTP's use but it's more convenient to
29 * maintain them here.
30 */
31
32#include "common.h"
33
34#include <unistd.h>
35#include <gcrypt.h>
36
37#include "uaudio.h"
38#include "mem.h"
39#include "log.h"
40#include "syscalls.h"
41#include "timeval.h"
42
43/** @brief Sample timestamp
44 *
45 * This is the timestamp that will be used on the next outbound packet.
46 *
47 * The timestamp in an RTP packet header is only 32 bits wide. With 44100Hz
48 * stereo, that only gives about half a day before wrapping, which is not
49 * particularly convenient for certain debugging purposes. Therefore the
50 * timestamp is maintained as a 64-bit integer, giving around six million years
51 * before wrapping, and truncated to 32 bits when transmitting.
52 */
53uint64_t uaudio_schedule_timestamp;
54
55/** @brief Actual time corresponding to @ref uaudio_schedule_timestamp
56 *
57 * This is the time, on this machine, at which the sample at @ref
58 * uaudio_schedule_timestamp ought to be sent, interpreted as the time the last
59 * packet was sent plus the time length of the packet. */
60static struct timeval uaudio_schedule_timeval;
61
62/** @brief Set when we (re-)activate, to provoke timestamp resync */
63int uaudio_schedule_reactivated;
64
65/** @brief Delay threshold in microseconds
66 *
67 * uaudio_schedule_play() never attempts to introduce a delay shorter than this.
68 */
69static int64_t uaudio_schedule_delay_threshold;
70
71/** @brief Time for current packet */
72static struct timeval uaudio_schedule_now;
73
74/** @brief Synchronize playback operations against real time
75 *
76 * This function sleeps as necessary to rate-limit playback operations to match
77 * the actual playback rate. It also maintains @ref uaudio_schedule_timestamp
78 * as an arbitrarily-based sample counter, for use by RTP.
79 *
80 * You should call this in your API's @ref uaudio_playcallback before writing
81 * and call uaudio_schedule_update() afterwards.
82 */
83void uaudio_schedule_synchronize(void) {
84retry:
85 xgettimeofday(&uaudio_schedule_now, NULL);
86 if(uaudio_schedule_reactivated) {
87 /* We've been deactivated for some unknown interval. We need to advance
88 * rtp_timestamp to account for the dead air. */
89 /* On the first run through we'll set the start time. */
90 if(!uaudio_schedule_timeval.tv_sec)
91 uaudio_schedule_timeval = uaudio_schedule_now;
92 /* See how much time we missed.
93 *
94 * This will be 0 on the first run through, in which case we'll not modify
95 * anything.
96 *
97 * It'll be negative in the (rare) situation where the deactivation
98 * interval is shorter than the last packet we sent. In this case we wait
99 * for that much time and then return having sent no samples, which will
100 * cause uaudio_play_thread_fn() to retry.
101 *
102 * In the normal case it will be positive.
103 */
104 const int64_t delay = tvsub_us(uaudio_schedule_now,
105 uaudio_schedule_timeval); /* microseconds */
106 if(delay < 0) {
107 usleep(-delay);
108 goto retry;
109 }
110 /* Advance the RTP timestamp to the present. With 44.1KHz stereo this will
111 * overflow the intermediate value with a delay of a bit over 6 years.
112 * This seems acceptable. */
113 uint64_t update = (delay * uaudio_rate * uaudio_channels) / 1000000;
114 /* Don't throw off channel synchronization */
115 update -= update % uaudio_channels;
116 /* We log nontrivial changes */
117 if(update)
118 info("advancing uaudio_schedule_timeval by %"PRIu64" samples", update);
119 uaudio_schedule_timestamp += update;
120 uaudio_schedule_timeval = uaudio_schedule_now;
121 uaudio_schedule_reactivated = 0;
122 } else {
123 /* Chances are we've been called right on the heels of the previous packet.
124 * If we just sent packets as fast as we got audio data we'd get way ahead
125 * of the player and some buffer somewhere would fill (or at least become
126 * unreasonably large).
127 *
128 * First find out how far ahead of the target time we are.
129 */
130 const int64_t ahead = tvsub_us(uaudio_schedule_timeval,
131 uaudio_schedule_now); /* microseconds */
132 /* Only delay at all if we are nontrivially ahead. */
133 if(ahead > uaudio_schedule_delay_threshold) {
134 /* Don't delay by the full amount */
135 usleep(ahead - uaudio_schedule_delay_threshold / 2);
136 /* Refetch time (so we don't get out of step with reality) */
137 xgettimeofday(&uaudio_schedule_now, NULL);
138 }
139 }
140}
141
142/** @brief Update schedule after writing
143 *
144 * Called by your API's @ref uaudio_playcallback after sending audio data (to a
145 * subprocess or network or whatever). A separate function so that the caller
146 * doesn't have to know how many samples they're going to write until they've
147 * done so.
148 */
149void uaudio_schedule_update(size_t written_samples) {
150 /* uaudio_schedule_timestamp and uaudio_schedule_timestamp are supposed to
151 * refer to the first sample of the next packet */
152 uaudio_schedule_timestamp += written_samples;
153 const unsigned usec = (uaudio_schedule_timeval.tv_usec
154 + 1000000 * written_samples / (uaudio_rate
155 * uaudio_channels));
156 /* ...will only overflow 32 bits if one packet is more than about half an
157 * hour long, which is not plausible. */
158 uaudio_schedule_timeval.tv_sec += usec / 1000000;
159 uaudio_schedule_timeval.tv_usec = usec % 1000000;
160}
161
162/** @brief Initialize audio scheduling
163 *
164 * Should be called from your API's @c start callback.
165 */
166void uaudio_schedule_init(void) {
167 gcry_create_nonce(&uaudio_schedule_timestamp,
168 sizeof uaudio_schedule_timestamp);
169 /* uaudio_schedule_play() will spot this and choose an initial value */
170 uaudio_schedule_timeval.tv_sec = 0;
171}
172
173/*
174Local Variables:
175c-basic-offset:2
176comment-column:40
177fill-column:79
178indent-tabs-mode:nil
179End:
180*/