Commit | Line | Data |
---|---|---|
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 | 65 | static 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 | 71 | struct timeval base; |
ec57f6c9 RK |
72 | |
73 | /** @brief Synchronize playback operations against real time | |
b1f6ca8c | 74 | * @return Sample number |
ec57f6c9 | 75 | * |
ec57f6c9 | 76 | */ |
b1f6ca8c RK |
77 | uint32_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 |
145 | void 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 | */ | |
153 | void 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 | /* | |
159 | Local Variables: | |
160 | c-basic-offset:2 | |
161 | comment-column:40 | |
162 | fill-column:79 | |
163 | indent-tabs-mode:nil | |
164 | End: | |
165 | */ |