#
# This file is part of DisOrder.
-# Copyright (C) 2004-2010 Richard Kettlewell
+# Copyright (C) 2004-2010, 2012, 2013 Richard Kettlewell
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
speaker-protocol.c speaker-protocol.h \
split.c split.h \
strptime.c strptime.h \
- syscalls.c syscalls.h \
+ syscalls.c syscallsrt.c syscalls.h \
common.h \
table.c table.h \
timeval.h \
/*
* This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2004, 2005, 2007, 2008, 2013 Richard Kettlewell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <sys/socket.h>
#include <sys/time.h>
#include <signal.h>
+#include <time.h>
#include "syscalls.h"
#include "log.h"
return tv.tv_sec;
}
+void xnanosleep(const struct timespec *req, struct timespec *rem) {
+ mustnotbeminus1("nanosleep", nanosleep(req, rem));
+}
+
/*
Local Variables:
c-basic-offset:2
/*
* This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2004, 2005, 2007-2009, 2013 Richard Kettlewell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
int xnice(int);
void xgettimeofday(struct timeval *, struct timezone *);
time_t xtime(time_t *when);
+void xgettime(clockid_t clk_id, struct timespec *tp);
+void xnanosleep(const struct timespec *req, struct timespec *rem);
/* the above all call @fatal@ if the system call fails */
void nonblock(int fd);
--- /dev/null
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2013 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/syscallsrt.c
+ * @brief Error-checking library call wrappers
+ */
+#include "common.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <time.h>
+
+#include "syscalls.h"
+#include "log.h"
+#include "printf.h"
+
+void xgettime(clockid_t clk_id, struct timespec *tp) {
+ mustnotbeminus1("clock_gettime", clock_gettime(clk_id, tp));
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+End:
+*/
/*
* This file is part of DisOrder.
- * Copyright (C) 2007-2009 Richard Kettlewell
+ * Copyright (C) 2007-2009, 2013 Richard Kettlewell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <time.h>
#include <sys/time.h>
+#include <math.h>
static inline struct timeval tvsub(const struct timeval a,
const struct timeval b) {
return !tvgt(a, b);
}
+/** @brief Return the sum of two timespecs */
+static inline struct timespec tsadd(const struct timespec a,
+ const struct timespec b) {
+ struct timespec r;
+
+ r.tv_sec = a.tv_sec + b.tv_sec;
+ r.tv_nsec = a.tv_nsec + b.tv_nsec;
+ if(r.tv_nsec < 0) {
+ r.tv_nsec += 1000000;
+ r.tv_sec--;
+ }
+ if(r.tv_nsec > 999999) {
+ r.tv_nsec -= 1000000;
+ r.tv_sec++;
+ }
+ return r;
+}
+
+/** @brief Subtract one timespec from another */
+static inline struct timespec tssub(const struct timespec a,
+ const struct timespec b) {
+ struct timespec r;
+
+ r.tv_sec = a.tv_sec - b.tv_sec;
+ r.tv_nsec = a.tv_nsec - b.tv_nsec;
+ if(r.tv_nsec < 0) {
+ r.tv_nsec += 1000000;
+ r.tv_sec--;
+ }
+ if(r.tv_nsec > 999999) {
+ r.tv_nsec -= 1000000;
+ r.tv_sec++;
+ }
+ return r;
+}
+
+/** @brief Convert a timespec to a double */
+static inline double ts_to_double(const struct timespec ts) {
+ return ts.tv_sec + ts.tv_nsec / 1000000000.0;
+}
+
+/** @brief Convert a double to a timespec */
+static inline struct timespec double_to_ts(double n) {
+ double i, f;
+ struct timespec r;
+ f = modf(n, &i);
+ r.tv_sec = i;
+ r.tv_nsec = 1000000000 * f;
+ return r;
+}
+
#endif /* TIMEVAL_H */
/*
/*
* This file is part of DisOrder.
- * Copyright (C) 2009 Richard Kettlewell
+ * Copyright (C) 2009, 2013 Richard Kettlewell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include "uaudio.h"
#include "log.h"
#include "mem.h"
+#include "syscalls.h"
+#include "timeval.h"
/** @brief Number of buffers
*
return NULL;
}
+static size_t uaudio_play_samples(void *buffer, size_t samples, unsigned flags) {
+ static struct timespec base;
+ static int64_t frames_supplied;
+ struct timespec now;
+ struct timespec delay_ts;
+ double target, delay;
+
+ if(!base.tv_sec)
+ xgettime(CLOCK_MONOTONIC, &base);
+ samples = uaudio_thread_play_callback(buffer, samples, flags);
+ frames_supplied += samples / uaudio_channels;
+ /* Set target to the approximate point at which we run out of buffered audio.
+ * If no buffer size has been specified, use 1/16th of a second. */
+ target = (frames_supplied - (uaudio_buffer ? uaudio_buffer : uaudio_rate / 16))
+ / (double)uaudio_rate + ts_to_double(base);
+ for(;;) {
+ xgettime(CLOCK_MONOTONIC, &now);
+ delay = target - ts_to_double(now);
+ if(delay <= 0) {
+ //putc('.', stderr);
+ break;
+ }
+ //putc('!', stderr);
+ /*
+ fprintf(stderr, "frames supplied %ld (%lds) base %f target %f now %f want delay %g\n",
+ frames_supplied,
+ frames_supplied / uaudio_rate,
+ ts_to_double(base),
+ target,
+ ts_to_double(now),
+ delay);
+ */
+ delay_ts = double_to_ts(delay);
+ xnanosleep(&delay_ts, NULL);
+ }
+ return samples;
+}
+
/** @brief Background thread for audio playing
*
* This thread plays data as long as there is something to play. So the
unsigned flags = UAUDIO_PAUSED;
if(last_flags & UAUDIO_PLAYING)
flags |= UAUDIO_PAUSE;
- uaudio_thread_play_callback(zero, uaudio_thread_max,
- last_flags = flags);
+ uaudio_play_samples(zero, uaudio_thread_max, last_flags = flags);
/* We expect the play callback to block for a reasonable period */
pthread_mutex_lock(&uaudio_thread_lock);
continue;
unsigned flags = UAUDIO_PLAYING;
if(last_flags & UAUDIO_PAUSED)
flags |= UAUDIO_RESUME;
- played += uaudio_thread_play_callback((char *)b->samples
- + played * uaudio_sample_size,
- b->nsamples - played,
- last_flags = flags);
+ played += uaudio_play_samples((char *)b->samples
+ + played * uaudio_sample_size,
+ b->nsamples - played,
+ last_flags = flags);
}
pthread_mutex_lock(&uaudio_thread_lock);
/* Move to next buffer */
/*
* This file is part of DisOrder.
- * Copyright (C) 2009 Richard Kettlewell
+ * Copyright (C) 2009, 2013 Richard Kettlewell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
/** @brief Whether samples are signed or unsigned */
int uaudio_signed;
+/** @brief Frames of buffer to tolerate inside chosen API */
+int uaudio_buffer;
+
/** @brief Sample size in bytes
*
* NB one sample is a single point sample; up to @c uaudio_channels samples may
/*
* This file is part of DisOrder.
- * Copyright (C) 2009 Richard Kettlewell
+ * Copyright (C) 2009, 2013 Richard Kettlewell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
extern int uaudio_channels;
extern int uaudio_signed;
extern size_t uaudio_sample_size;
+extern int uaudio_buffer;
/** @brief Callback to get audio data
* @param buffer Where to put audio data