-void uaudio_schedule_synchronize(void) {
-retry:
- xgettimeofday(&uaudio_schedule_now, NULL);
- if(uaudio_schedule_reactivated) {
- /* We've been deactivated for some unknown interval. We need to advance
- * rtp_timestamp to account for the dead air. */
- /* On the first run through we'll set the start time. */
- if(!uaudio_schedule_timeval.tv_sec)
- uaudio_schedule_timeval = uaudio_schedule_now;
- /* See how much time we missed.
- *
- * This will be 0 on the first run through, in which case we'll not modify
- * anything.
- *
- * It'll be negative in the (rare) situation where the deactivation
- * interval is shorter than the last packet we sent. In this case we wait
- * for that much time and then return having sent no samples, which will
- * cause uaudio_play_thread_fn() to retry.
- *
- * In the normal case it will be positive.
- */
- const int64_t delay = tvsub_us(uaudio_schedule_now,
- uaudio_schedule_timeval); /* microseconds */
- if(delay < 0) {
- usleep(-delay);
- goto retry;
- }
- /* Advance the RTP timestamp to the present. With 44.1KHz stereo this will
- * overflow the intermediate value with a delay of a bit over 6 years.
- * This seems acceptable. */
- uint64_t update = (delay * uaudio_rate * uaudio_channels) / 1000000;
- /* Don't throw off channel synchronization */
- update -= update % uaudio_channels;
- /* We log nontrivial changes */
- if(update)
- info("advancing uaudio_schedule_timeval by %"PRIu64" samples", update);
- uaudio_schedule_timestamp += update;
- uaudio_schedule_timeval = uaudio_schedule_now;
- uaudio_schedule_reactivated = 0;
+uint32_t uaudio_schedule_sync(void) {
+ const unsigned rate = uaudio_rate * uaudio_channels;
+ struct timeval now;
+
+ xgettimeofday(&now, NULL);
+ /* If we're just starting then we might as well send as much as possible
+ * straight away. */
+ if(!base.tv_sec) {
+ base = now;
+ return timestamp;
+ }
+ /* Calculate how many microseconds ahead of the base time we are */
+ uint64_t us = tvsub_us(now, base);
+ /* Calculate how many samples that is */
+ uint64_t samples = us * rate / 1000000;
+ /* So...
+ *
+ * We've actually sent 'timestamp' samples so far.
+ *
+ * We OUGHT to have sent 'samples' samples so far.
+ *
+ * Suppose it's the SECOND call. timestamp will be (say) 716. 'samples'
+ * will be (say) 10 - there's been a bit of scheduling delay. So in that
+ * case we should wait for 716-10=706 samples worth of time before we can
+ * even send one sample.
+ *
+ * So we wait that long and send our 716 samples.
+ *
+ * On the next call we'll have timestamp=1432 and samples=726, say. So we
+ * wait and send again.
+ *
+ * On the next call there's been a bit of a delay. timestamp=2148 but
+ * samples=2200. So we send our 716 samples immediately.
+ *
+ * If the delay had been longer we might sent further packets back to back to
+ * make up for it.
+ *
+ * Now timestamp=2864 and samples=2210 (say). Now we're back to waiting.
+ */
+ if(samples < timestamp) {
+ /* We should delay a bit */
+ int64_t wait_samples = timestamp - samples;
+ int64_t wait_ns = wait_samples * 1000000000 / rate;
+
+ struct timespec ts[1];
+ ts->tv_sec = wait_ns / 1000000000;
+ ts->tv_nsec = wait_ns % 1000000000;
+#if 0
+ fprintf(stderr,
+ "samples=%8"PRIu64" timestamp=%8"PRIu64" wait=%"PRId64" (%"PRId64"ns)\n",
+ samples, timestamp, wait_samples, wait_ns);
+#endif
+ while(nanosleep(ts, ts) < 0 && errno == EINTR)
+ ;