2 * This file is part of DisOrder.
3 * Copyright (C) 2009 Richard Kettlewell
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.
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.
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/>.
18 /** @file lib/uaudio-schedule.c
19 * @brief Scheduler for RTP and command backends
21 * These functions ensure that audio is only written at approximately the rate
22 * it should play at, allowing pause to function properly.
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
28 * The sequence numbers are intended for RTP's use but it's more convenient to
43 /** @brief Sample timestamp
45 * This is the timestamp that will be used on the next outbound packet.
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.
53 uint64_t uaudio_schedule_timestamp
;
55 /** @brief Actual time corresponding to @ref uaudio_schedule_timestamp
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. */
60 static struct timeval uaudio_schedule_timeval
;
62 /** @brief Set when we (re-)activate, to provoke timestamp resync */
63 int uaudio_schedule_reactivated
;
65 /** @brief Delay threshold in microseconds
67 * uaudio_schedule_play() never attempts to introduce a delay shorter than this.
69 static int64_t uaudio_schedule_delay_threshold
;
71 /** @brief Time for current packet */
72 static struct timeval uaudio_schedule_now
;
74 /** @brief Synchronize playback operations against real time
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.
80 * You should call this in your API's @ref uaudio_playcallback before writing
81 * and call uaudio_schedule_update() afterwards.
83 void uaudio_schedule_synchronize(void) {
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.
94 * This will be 0 on the first run through, in which case we'll not modify
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.
102 * In the normal case it will be positive.
104 const int64_t delay
= tvsub_us(uaudio_schedule_now
,
105 uaudio_schedule_timeval
); /* microseconds */
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 */
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;
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).
128 * First find out how far ahead of the target time we are.
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
);
142 /** @brief Update schedule after writing
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
149 void 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
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;
162 /** @brief Initialize audio scheduling
164 * Should be called from your API's @c start callback.
166 void 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;