chiark / gitweb /
--without-server builds should now work again.
[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.
b1f6ca8c
RK
30 *
31 * The basic idea:
32 * - we maintain a base time
33 * - we calculate from this how many samples SHOULD have been sent by now
34 * - we compare this with the number of samples sent so far
35 * - we use this to wait until we're ready to send something
36 * - it's up to the caller to send nothing, or send 0s, if it's supposed to
37 * be paused
38 *
39 * An implication of this is that the caller must still call
40 * uaudio_schedule_sync() when deactivated (paused) and pretend to send 0s.
ec57f6c9
RK
41 */
42
43#include "common.h"
44
45#include <unistd.h>
b1f6ca8c
RK
46#include <time.h>
47#include <errno.h>
ec57f6c9
RK
48
49#include "uaudio.h"
50#include "mem.h"
51#include "log.h"
52#include "syscalls.h"
53#include "timeval.h"
54
55/** @brief Sample timestamp
56 *
57 * This is the timestamp that will be used on the next outbound packet.
58 *
59 * The timestamp in an RTP packet header is only 32 bits wide. With 44100Hz
60 * stereo, that only gives about half a day before wrapping, which is not
61 * particularly convenient for certain debugging purposes. Therefore the
62 * timestamp is maintained as a 64-bit integer, giving around six million years
63 * before wrapping, and truncated to 32 bits when transmitting.
64 */
b1f6ca8c 65static uint64_t timestamp;
ec57f6c9 66
b1f6ca8c 67/** @brief Base time
ec57f6c9 68 *
b1f6ca8c 69 * This is the base time that corresponds to a timestamp of 0.
ec57f6c9 70 */
b1f6ca8c 71struct timeval base;
ec57f6c9
RK
72
73/** @brief Synchronize playback operations against real time
b1f6ca8c 74 * @return Sample number
ec57f6c9 75 *
ec57f6c9 76 */
b1f6ca8c
RK
77uint32_t uaudio_schedule_sync(void) {
78 const unsigned rate = uaudio_rate * uaudio_channels;
79 struct timeval now;
80
81 xgettimeofday(&now, NULL);
82 /* If we're just starting then we might as well send as much as possible
83 * straight away. */
84 if(!base.tv_sec) {
85 base = now;
86 return timestamp;
87 }
88 /* Calculate how many microseconds ahead of the base time we are */
89 uint64_t us = tvsub_us(now, base);
90 /* Calculate how many samples that is */
91 uint64_t samples = us * rate / 1000000;
92 /* So...
93 *
94 * We've actually sent 'timestamp' samples so far.
95 *
96 * We OUGHT to have sent 'samples' samples so far.
97 *
98 * Suppose it's the SECOND call. timestamp will be (say) 716. 'samples'
99 * will be (say) 10 - there's been a bit of scheduling delay. So in that
100 * case we should wait for 716-10=706 samples worth of time before we can
101 * even send one sample.
102 *
103 * So we wait that long and send our 716 samples.
104 *
105 * On the next call we'll have timestamp=1432 and samples=726, say. So we
106 * wait and send again.
107 *
108 * On the next call there's been a bit of a delay. timestamp=2148 but
109 * samples=2200. So we send our 716 samples immediately.
110 *
111 * If the delay had been longer we might sent further packets back to back to
112 * make up for it.
113 *
114 * Now timestamp=2864 and samples=2210 (say). Now we're back to waiting.
115 */
116 if(samples < timestamp) {
117 /* We should delay a bit */
118 int64_t wait_samples = timestamp - samples;
119 int64_t wait_ns = wait_samples * 1000000000 / rate;
120
121 struct timespec ts[1];
122 ts->tv_sec = wait_ns / 1000000000;
123 ts->tv_nsec = wait_ns % 1000000000;
124#if 0
125 fprintf(stderr,
126 "samples=%8"PRIu64" timestamp=%8"PRIu64" wait=%"PRId64" (%"PRId64"ns)\n",
127 samples, timestamp, wait_samples, wait_ns);
128#endif
129 while(nanosleep(ts, ts) < 0 && errno == EINTR)
130 ;
ec57f6c9 131 } else {
b1f6ca8c
RK
132#if 0
133 fprintf(stderr, "samples=%8"PRIu64" timestamp=%8"PRIu64"\n",
134 samples, timestamp);
135#endif
ec57f6c9 136 }
b1f6ca8c
RK
137 /* If samples >= timestamp then it's time, or gone time, to play the
138 * timestamp'th sample. So we return immediately. */
139 return timestamp;
ec57f6c9
RK
140}
141
b1f6ca8c 142/** @brief Report how many samples we actually sent
c0f52b2c 143 * @param nsamples_sent Number of samples sent
ec57f6c9 144 */
1c140a81
RK
145void uaudio_schedule_sent(size_t nsamples_sent) {
146 timestamp += nsamples_sent;
ec57f6c9
RK
147}
148
149/** @brief Initialize audio scheduling
150 *
151 * Should be called from your API's @c start callback.
152 */
153void uaudio_schedule_init(void) {
ec57f6c9 154 /* uaudio_schedule_play() will spot this and choose an initial value */
b1f6ca8c 155 base.tv_sec = 0;
ec57f6c9
RK
156}
157
158/*
159Local Variables:
160c-basic-offset:2
161comment-column:40
162fill-column:79
163indent-tabs-mode:nil
164End:
165*/