speaker-network to transmit at a constant rate instead of bursting
once a second.
static int alsa_slots, alsa_nslots = -1;
/** @brief Fill in poll fd array for ALSA */
static int alsa_slots, alsa_nslots = -1;
/** @brief Fill in poll fd array for ALSA */
-static void alsa_beforepoll(void) {
+static void alsa_beforepoll(int attribute((unused)) *timeoutp) {
/* We send sample data to ALSA as fast as it can accept it, relying on
* the fact that it has a relatively small buffer to minimize pause
* latency. */
/* We send sample data to ALSA as fast as it can accept it, relying on
* the fact that it has a relatively small buffer to minimize pause
* latency. */
}
/** @brief Update poll array for writing to subprocess */
}
/** @brief Update poll array for writing to subprocess */
-static void command_beforepoll(void) {
+static void command_beforepoll(int attribute((unused)) *timeoutp) {
/* We send sample data to the subprocess as fast as it can accept it.
* This isn't ideal as pause latency can be very high as a result. */
if(cmdfd >= 0)
/* We send sample data to the subprocess as fast as it can accept it.
* This isn't ideal as pause latency can be very high as a result. */
if(cmdfd >= 0)
}
/** @brief Fill in poll fd array for Core Audio */
}
/** @brief Fill in poll fd array for Core Audio */
-static void coreaudio_beforepoll(void) {
+static void coreaudio_beforepoll(int attribute((unused)) *timeoutp) {
pfd_slot = addfd(pfd[1], POLLOUT);
}
pfd_slot = addfd(pfd[1], POLLOUT);
}
static int bfd_slot;
/** @brief Set up poll array for network play */
static int bfd_slot;
/** @brief Set up poll array for network play */
-static void network_beforepoll(void) {
+static void network_beforepoll(int *timeoutp) {
struct timeval now;
uint64_t target_us;
uint64_t target_rtp_time;
struct timeval now;
uint64_t target_us;
uint64_t target_rtp_time;
+ const int64_t samples_per_second = config->sample_format.rate
+ * config->sample_format.channels;
const int64_t samples_ahead = ((uint64_t)RTP_AHEAD_MS
const int64_t samples_ahead = ((uint64_t)RTP_AHEAD_MS
- * config->sample_format.rate
- * config->sample_format.channels
+ int64_t lead, ahead_ms;
/* If we're starting then initialize the base time */
if(!rtp_time)
/* If we're starting then initialize the base time */
if(!rtp_time)
target_rtp_time = (target_us * config->sample_format.rate
* config->sample_format.channels)
/ 1000000;
target_rtp_time = (target_us * config->sample_format.rate
* config->sample_format.channels)
/ 1000000;
- if((int64_t)(rtp_time - target_rtp_time) < samples_ahead)
+ lead = rtp_time - target_rtp_time;
+ if(lead < samples_ahead)
+ /* We've not reached the desired lead, write as fast as we can */
bfd_slot = addfd(bfd, POLLOUT);
bfd_slot = addfd(bfd, POLLOUT);
+ else {
+ /* We've reached the desired lead, we can afford to wait a bit even if the
+ * IP stack thinks it can accept more. */
+ ahead_ms = 1000 * (lead - samples_ahead) / samples_per_second;
+ if(ahead_ms < *timeoutp)
+ *timeoutp = ahead_ms;
+ }
}
/** @brief Process poll() results for network play */
}
/** @brief Process poll() results for network play */
static int oss_slot;
/** @brief Fill in poll fd array for OSS */
static int oss_slot;
/** @brief Fill in poll fd array for OSS */
-static void oss_beforepoll(void) {
+static void oss_beforepoll(int attribute((unused)) *timeoutp) {
oss_slot = addfd(ossfd, POLLOUT|POLLERR);
}
oss_slot = addfd(ossfd, POLLOUT|POLLERR);
}
* instead, but the post-poll code will cope even if it's
* device_closed. */
if(device_state == device_open)
* instead, but the post-poll code will cope even if it's
* device_closed. */
if(device_state == device_open)
+ backend->beforepoll(&timeout);
}
/* If any other tracks don't have a full buffer, try to read sample data
* from them. We do this last of all, so that if we run out of slots,
}
/* If any other tracks don't have a full buffer, try to read sample data
* from them. We do this last of all, so that if we run out of slots,
void (*deactivate)(void);
/** @brief Called before poll()
void (*deactivate)(void);
/** @brief Called before poll()
+ * @param timeoutp Pointer to timeout
- * Called before the call to poll(). Should call addfd() to update
- * the FD array and stash the slot number somewhere safe. This will
- * only be called if @ref device_state = @ref device_open.
+ * Called before the call to poll().
+ *
+ * If desirable, should call addfd() to update the FD array and stash the
+ * slot number somewhere safe. This will only be called if @ref device_state
+ * is @ref device_open.
+ *
+ * @p timeoutp points to the poll timeout value in milliseconds. It may be
+ * reduced, but never increased.
- void (*beforepoll)(void);
+ void (*beforepoll)(int *timeoutp);
/** @brief Called after poll()
* @return 1 if output device ready for play, 0 otherwise
/** @brief Called after poll()
* @return 1 if output device ready for play, 0 otherwise