if (ret == 0) {
*state = snd_pcm_status_get_state(alsa_snd_pcm_status);
- if ((*state == SND_PCM_STATE_SUSPENDED) || (*state == SND_PCM_STATE_XRUN)) {
- debug(1, "alsa: recovering from SND_PCM_STATE_* %d in delay_prep_and_status.", *state);
- ret = snd_pcm_recover(alsa_handle, ret, 1);
- if (ret == 0) {
- ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status);
- if (ret == 0)
- *state = snd_pcm_status_get_state(alsa_snd_pcm_status);
- if (*state != SND_PCM_STATE_PREPARED) {
- debug(1, "alsa: snd_pcm_recover failed -- have to use snd_pcm_prepare.");
- ret = snd_pcm_prepare(alsa_handle);
- if (ret == 0)
- ret = snd_pcm_recover(alsa_handle, ret, 1);
- if (ret == 0) {
- ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status);
- if (ret == 0)
- *state = snd_pcm_status_get_state(alsa_snd_pcm_status);
- }
- }
- }
- }
-
- // here, the device must be either SND_PCM_STATE_RUNNING
- // or SND_PCM_STATE_PREPARED or SND_PCM_STATE_DRAINING
-
- if (ret == 0) {
-
- if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) {
+ if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) {
- snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp);
- *delay = snd_pcm_status_get_delay(alsa_snd_pcm_status);
+ snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp);
+ *delay = snd_pcm_status_get_delay(alsa_snd_pcm_status);
- if (*state == SND_PCM_STATE_DRAINING)
- debug(1, "alsa: draining with a delay of %d.", delay);
+ if (*state == SND_PCM_STATE_DRAINING)
+ debug(1, "alsa: draining with a delay of %d.", delay);
- clock_gettime(CLOCK_MONOTONIC, &tn);
- uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec;
- uint64_t update_timestamp_ns =
- update_timestamp.tv_sec * (uint64_t)1000000000 + update_timestamp.tv_nsec;
+ clock_gettime(CLOCK_MONOTONIC, &tn);
+ uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec;
+ uint64_t update_timestamp_ns =
+ update_timestamp.tv_sec * (uint64_t)1000000000 + update_timestamp.tv_nsec;
- // see if it's stalled
+ // see if it's stalled
- if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == *delay)) {
- // hasn't outputted anything since the last call to delay()
+ if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == *delay)) {
+ // hasn't outputted anything since the last call to delay()
- if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) ||
- ((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) {
- ret = sps_extra_code_output_stalled;
- }
- } else {
- stall_monitor_start_time = update_timestamp_ns;
- stall_monitor_frame_count = *delay;
+ if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) ||
+ ((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) {
+ ret = sps_extra_code_output_stalled;
}
+ } else {
+ stall_monitor_start_time = update_timestamp_ns;
+ stall_monitor_frame_count = *delay;
+ }
- if (ret == 0) {
- uint64_t delta = time_now_ns - update_timestamp_ns;
-
- uint64_t frames_played_since_last_interrupt =
- ((uint64_t)desired_sample_rate * delta) / 1000000000;
- snd_pcm_sframes_t frames_played_since_last_interrupt_sized =
- frames_played_since_last_interrupt;
-
- *delay = *delay - frames_played_since_last_interrupt_sized;
- }
- } else { // not running, thus no delay information, thus can't check for stall
- stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush
- stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes
+ if (ret == 0) {
+ uint64_t delta = time_now_ns - update_timestamp_ns;
- // not running, thus no delay information, thus can't check for frame rates
- frame_index = 0; // we'll be starting over...
- measurement_data_is_valid = 0;
+ uint64_t frames_played_since_last_interrupt =
+ ((uint64_t)desired_sample_rate * delta) / 1000000000;
+ snd_pcm_sframes_t frames_played_since_last_interrupt_sized =
+ frames_played_since_last_interrupt;
- if ((*state != SND_PCM_STATE_PREPARED)) {
- debug(1, "alsa: can't get device into valid state in delay. State is %d.", *state);
- ret = sps_extra_code_output_state_cannot_make_ready;
- }
+ *delay = *delay - frames_played_since_last_interrupt_sized;
}
+ } else { // not running, thus no delay information, thus can't check for stall
+ *delay = 0;
+ stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush
+ stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes
+
+ // not running, thus no delay information, thus can't check for frame rates
+ frame_index = 0; // we'll be starting over...
+ measurement_data_is_valid = 0;
}
}
return ret;
ret = delay_prep_and_status(&state, &my_delay);
if (ret == 0) { // will be non-zero if an error or a stall
- if ((state == SND_PCM_STATE_PREPARED) || (state == SND_PCM_STATE_RUNNING)) {
- if ((samples != 0) && (buf != NULL)) {
+ if ((samples != 0) && (buf != NULL)) {
- // debug(3, "write %d frames.", samples);
- ret = alsa_pcm_write(alsa_handle, buf, samples);
- if (ret == samples) {
- stall_monitor_frame_count += samples;
+ // debug(3, "write %d frames.", samples);
+ ret = alsa_pcm_write(alsa_handle, buf, samples);
+ if (ret == samples) {
+ stall_monitor_frame_count += samples;
- if (frame_index == 0) {
- frames_sent_for_playing = samples;
- } else {
- frames_sent_for_playing += samples;
- }
+ if (frame_index == 0) {
+ frames_sent_for_playing = samples;
+ } else {
+ frames_sent_for_playing += samples;
+ }
- const uint64_t start_measurement_from_this_frame =
- (2 * 44100) / 352; // two seconds of frames…
+ const uint64_t start_measurement_from_this_frame =
+ (2 * 44100) / 352; // two seconds of frames…
- frame_index++;
+ frame_index++;
- if ((frame_index == start_measurement_from_this_frame) ||
- ((frame_index > start_measurement_from_this_frame) && (frame_index % 32 == 0))) {
+ if ((frame_index == start_measurement_from_this_frame) ||
+ ((frame_index > start_measurement_from_this_frame) && (frame_index % 32 == 0))) {
- measurement_time = get_absolute_time_in_fp();
- frames_played_at_measurement_time = frames_sent_for_playing - my_delay - samples;
+ measurement_time = get_absolute_time_in_fp();
+ frames_played_at_measurement_time = frames_sent_for_playing - my_delay - samples;
- if (frame_index == start_measurement_from_this_frame) {
- // debug(1, "Start frame counting");
- frames_played_at_measurement_start_time = frames_played_at_measurement_time;
- measurement_start_time = measurement_time;
- measurement_data_is_valid = 1;
+ if (frame_index == start_measurement_from_this_frame) {
+ // debug(1, "Start frame counting");
+ frames_played_at_measurement_start_time = frames_played_at_measurement_time;
+ measurement_start_time = measurement_time;
+ measurement_data_is_valid = 1;
+ }
+ }
+ } else {
+ debug(1, "alsa: error %d writing %d samples to alsa device.", ret, samples);
+ if (ret == -EPIPE) { /* underrun */
+ ret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 1 : 0);
+ if (ret < 0) {
+ ret = snd_pcm_prepare(alsa_handle);
+ if (ret < 0)
+ warn("alsa: can't recover from SND_PCM_STATE_XRUN, snd_pcm_recover() and "
+ "snd_pcm_prepare() failed: %s.",
+ snd_strerror(ret));
+ } else if (ret == -ESTRPIPE) { /* suspended */
+ while ((ret = snd_pcm_resume(alsa_handle)) == -EAGAIN)
+ sleep(1); /* wait until the suspend flag is released */
+ if (ret < 0) {
+ ret = snd_pcm_prepare(alsa_handle);
+ if (ret < 0)
+ warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, snd_pcm_prepare() "
+ "failed: %s.",
+ snd_strerror(ret));
}
}
- } else {
- debug(1, "alsa: error writing to alsa device.");
}
}
} else {