static void CheckXRUN(IDsDriverBufferImpl* This) { snd_pcm_state_t state = snd_pcm_state(This->pcm); snd_pcm_sframes_t delay; int err; snd_pcm_hwsync(This->pcm); snd_pcm_delay(This->pcm, &delay); if ( state == SND_PCM_STATE_XRUN ) { err = snd_pcm_prepare(This->pcm); CommitAll(This); snd_pcm_start(This->pcm); WARN("xrun occurred\n"); if ( err < 0 ) ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err)); } else if ( state == SND_PCM_STATE_SUSPENDED ) { int err = snd_pcm_resume(This->pcm); TRACE("recovery from suspension occurred\n"); if (err < 0 && err != -EAGAIN){ err = snd_pcm_prepare(This->pcm); if (err < 0) ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err)); } } else if ( state != SND_PCM_STATE_RUNNING ) { FIXME("Unhandled state: %d\n", state); } }
static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, struct snd_pcm_sync_ptr32 __user *src) { struct snd_pcm_runtime *runtime = substream->runtime; volatile struct snd_pcm_mmap_status *status; volatile struct snd_pcm_mmap_control *control; u32 sflags; struct snd_pcm_mmap_control scontrol; struct snd_pcm_mmap_status sstatus; snd_pcm_uframes_t boundary; int err; if (snd_BUG_ON(!runtime)) return -EINVAL; if (get_user(sflags, &src->flags) || get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || get_user(scontrol.avail_min, &src->c.control.avail_min)) return -EFAULT; if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { err = snd_pcm_hwsync(substream); if (err < 0) return err; } status = runtime->status; control = runtime->control; boundary = recalculate_boundary(runtime); if (! boundary) boundary = 0x7fffffff; snd_pcm_stream_lock_irq(substream); /* FIXME: we should consider the boundary for the sync from app */ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) control->appl_ptr = scontrol.appl_ptr; else scontrol.appl_ptr = control->appl_ptr % boundary; if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) control->avail_min = scontrol.avail_min; else scontrol.avail_min = control->avail_min; sstatus.state = status->state; sstatus.hw_ptr = status->hw_ptr % boundary; sstatus.tstamp = status->tstamp; sstatus.suspended_state = status->suspended_state; sstatus.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); if (put_user(sstatus.state, &src->s.status.state) || put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) || put_user(sstatus.suspended_state, &src->s.status.suspended_state) || compat_put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) || put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || put_user(scontrol.avail_min, &src->c.control.avail_min)) return -EFAULT; return 0; }
static int snd_pcm_dmix_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dmix) { int err; snd_pcm_hwsync(dmix->spcm); reset_slave_ptr(pcm, dmix); err = snd_timer_start(dmix->timer); if (err < 0) return err; dmix->state = SND_PCM_STATE_RUNNING; return 0; }
static int snd_pcm_dshare_start_timer(snd_pcm_direct_t *dshare) { int err; snd_pcm_hwsync(dshare->spcm); dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; err = snd_timer_start(dshare->timer); if (err < 0) return err; dshare->state = SND_PCM_STATE_RUNNING; return 0; }
/* * synchronize hardware pointer (hw_ptr) with ours */ static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail; snd_pcm_sframes_t diff; switch (snd_pcm_state(dshare->spcm)) { case SND_PCM_STATE_DISCONNECTED: dshare->state = SNDRV_PCM_STATE_DISCONNECTED; return -ENODEV; default: break; } if (dshare->slowptr) snd_pcm_hwsync(dshare->spcm); old_slave_hw_ptr = dshare->slave_hw_ptr; slave_hw_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; diff = slave_hw_ptr - old_slave_hw_ptr; if (diff == 0) /* fast path */ return 0; if (dshare->state != SND_PCM_STATE_RUNNING && dshare->state != SND_PCM_STATE_DRAINING) /* not really started yet - don't update hw_ptr */ return 0; if (diff < 0) { slave_hw_ptr += dshare->slave_boundary; diff = slave_hw_ptr - old_slave_hw_ptr; } dshare->hw_ptr += diff; dshare->hw_ptr %= pcm->boundary; // printf("sync ptr diff = %li\n", diff); if (pcm->stop_threshold >= pcm->boundary) /* don't care */ return 0; avail = snd_pcm_mmap_playback_avail(pcm); if (avail > dshare->avail_max) dshare->avail_max = avail; if (avail >= pcm->stop_threshold) { struct timeval tv; snd_timer_stop(dshare->timer); gettimeofday(&tv, 0); dshare->trigger_tstamp.tv_sec = tv.tv_sec; dshare->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L; if (dshare->state == SND_PCM_STATE_RUNNING) { dshare->state = SND_PCM_STATE_XRUN; return -EPIPE; } dshare->state = SND_PCM_STATE_SETUP; /* clear queue to remove pending poll events */ snd_pcm_direct_clear_timer_queue(dshare); } return 0; }
static void *peeper(void *data) { int thread_no = (long)data; snd_pcm_sframes_t val; snd_pcm_status_t *stat; snd_htimestamp_t tstamp; int mode = running_mode, err; snd_pcm_status_alloca(&stat); while (running) { if (running_mode == MODE_RANDOM) mode = rand() % MODE_RANDOM; switch (mode) { case MODE_AVAIL_UPDATE: val = snd_pcm_avail_update(pcm); err = 0; break; case MODE_STATUS: err = snd_pcm_status(pcm, stat); val = snd_pcm_status_get_avail(stat); break; case MODE_HWSYNC: err = snd_pcm_hwsync(pcm); break; case MODE_TIMESTAMP: err = snd_pcm_htimestamp(pcm, (snd_pcm_uframes_t *)&val, &tstamp); break; default: err = snd_pcm_delay(pcm, &val); break; } if (quiet) continue; if (running_mode == MODE_RANDOM) { fprintf(stderr, "%d%c%s", thread_no, mode_suffix[mode], err ? "!" : ""); } else { if (show_value && mode != MODE_HWSYNC) fprintf(stderr, "\r%d ", (int)val); else fprintf(stderr, "%d%s", thread_no, err ? "!" : ""); } } return NULL; }
/* * synchronize hardware pointer (hw_ptr) with ours */ static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail; snd_pcm_sframes_t diff; switch (snd_pcm_state(dmix->spcm)) { case SND_PCM_STATE_DISCONNECTED: dmix->state = SND_PCM_STATE_DISCONNECTED; return -ENODEV; default: break; } if (dmix->slowptr) snd_pcm_hwsync(dmix->spcm); old_slave_hw_ptr = dmix->slave_hw_ptr; slave_hw_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr; diff = slave_hw_ptr - old_slave_hw_ptr; if (diff == 0) /* fast path */ return 0; if (dmix->state != SND_PCM_STATE_RUNNING && dmix->state != SND_PCM_STATE_DRAINING) /* not really started yet - don't update hw_ptr */ return 0; if (diff < 0) { slave_hw_ptr += dmix->slave_boundary; diff = slave_hw_ptr - old_slave_hw_ptr; } dmix->hw_ptr += diff; dmix->hw_ptr %= pcm->boundary; if (pcm->stop_threshold >= pcm->boundary) /* don't care */ return 0; avail = snd_pcm_mmap_playback_avail(pcm); if (avail > dmix->avail_max) dmix->avail_max = avail; if (avail >= pcm->stop_threshold) { snd_timer_stop(dmix->timer); gettimestamp(&dmix->trigger_tstamp, pcm->tstamp_type); if (dmix->state == SND_PCM_STATE_RUNNING) { dmix->state = SND_PCM_STATE_XRUN; return -EPIPE; } dmix->state = SND_PCM_STATE_SETUP; /* clear queue to remove pending poll events */ snd_pcm_direct_clear_timer_queue(dmix); } return 0; }
static int snd_pcm_dsnoop_start(snd_pcm_t *pcm) { snd_pcm_direct_t *dsnoop = pcm->private_data; int err; if (dsnoop->state != SND_PCM_STATE_PREPARED) return -EBADFD; snd_pcm_hwsync(dsnoop->spcm); snoop_timestamp(pcm); dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr; err = snd_timer_start(dsnoop->timer); if (err < 0) return err; dsnoop->state = SND_PCM_STATE_RUNNING; dsnoop->trigger_tstamp = dsnoop->update_tstamp; return 0; }
static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; switch (snd_pcm_state(dshare->spcm)) { case SND_PCM_STATE_DISCONNECTED: dshare->state = SNDRV_PCM_STATE_DISCONNECTED; return -ENODEV; default: break; } if (dshare->slowptr) snd_pcm_hwsync(dshare->spcm); return snd_pcm_dshare_sync_ptr0(pcm, *dshare->spcm->hw.ptr); }
/* * synchronize hardware pointer (hw_ptr) with ours */ static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm) { snd_pcm_direct_t *dsnoop = pcm->private_data; snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail; snd_pcm_sframes_t diff; switch (snd_pcm_state(dsnoop->spcm)) { case SND_PCM_STATE_DISCONNECTED: dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED; return -ENODEV; default: break; } if (dsnoop->slowptr) snd_pcm_hwsync(dsnoop->spcm); old_slave_hw_ptr = dsnoop->slave_hw_ptr; snoop_timestamp(pcm); slave_hw_ptr = dsnoop->slave_hw_ptr; diff = slave_hw_ptr - old_slave_hw_ptr; if (diff == 0) /* fast path */ return 0; if (diff < 0) { slave_hw_ptr += dsnoop->slave_boundary; diff = slave_hw_ptr - old_slave_hw_ptr; } snd_pcm_dsnoop_sync_area(pcm, old_slave_hw_ptr, diff); dsnoop->hw_ptr += diff; dsnoop->hw_ptr %= pcm->boundary; // printf("sync ptr diff = %li\n", diff); if (pcm->stop_threshold >= pcm->boundary) /* don't care */ return 0; if ((avail = snd_pcm_mmap_capture_hw_avail(pcm)) >= pcm->stop_threshold) { gettimestamp(&dsnoop->trigger_tstamp, pcm->monotonic); dsnoop->state = SND_PCM_STATE_XRUN; dsnoop->avail_max = avail; return -EPIPE; } if (avail > dsnoop->avail_max) dsnoop->avail_max = avail; return 0; }
static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; int err; switch (snd_pcm_state(dmix->spcm)) { case SND_PCM_STATE_DISCONNECTED: dmix->state = SND_PCM_STATE_DISCONNECTED; return -ENODEV; case SND_PCM_STATE_XRUN: if ((err = snd_pcm_direct_slave_recover(dmix)) < 0) return err; break; default: break; } if (snd_pcm_direct_client_chk_xrun(dmix, pcm)) return -EPIPE; if (dmix->slowptr) snd_pcm_hwsync(dmix->spcm); return snd_pcm_dmix_sync_ptr0(pcm, *dmix->spcm->hw.ptr); }
int snd_pcm_generic_hwsync(snd_pcm_t *pcm) { snd_pcm_generic_t *generic = pcm->private_data; return snd_pcm_hwsync(generic->slave); }
static void data_available_for_stream(pa_mainloop_api *a, pa_io_event *ioe, int fd, pa_io_event_flags_t events, void *userdata) { pa_stream *s = userdata; snd_pcm_sframes_t frame_count; size_t frame_size = pa_frame_size(&s->ss); char buf[16 * 1024]; int paused = g_atomic_int_get(&s->paused); if (events & (PA_IO_EVENT_INPUT | PA_IO_EVENT_OUTPUT)) { #if HAVE_SND_PCM_AVAIL frame_count = snd_pcm_avail(s->ph); #else snd_pcm_hwsync(s->ph); frame_count = snd_pcm_avail_update(s->ph); #endif if (frame_count < 0) { if (frame_count == -EBADFD) { // stream was closed return; } int cnt = 0, ret; do { cnt ++; ret = snd_pcm_recover(s->ph, frame_count, 1); } while (ret == -1 && errno == EINTR && cnt < 5); #if HAVE_SND_PCM_AVAIL frame_count = snd_pcm_avail(s->ph); #else snd_pcm_hwsync(s->ph); frame_count = snd_pcm_avail_update(s->ph); #endif if (frame_count < 0) { trace_error("%s, can't recover after failed snd_pcm_avail (%d)\n", __func__, (int)frame_count); return; } } } else { return; } if (events & PA_IO_EVENT_OUTPUT) { if (paused) { // client stream is corked. Pass silence to ALSA size_t bytecnt = MIN(sizeof(buf), frame_count * frame_size); memset(buf, 0, bytecnt); snd_pcm_writei(s->ph, buf, bytecnt / frame_size); } else { size_t writable_size = pa_stream_writable_size(s); if (s->write_cb && writable_size > 0) s->write_cb(s, writable_size, s->write_cb_userdata); size_t bytecnt = MIN(sizeof(buf), frame_count * frame_size); bytecnt = ringbuffer_read(s->rb, buf, bytecnt); if (bytecnt == 0) { // application is not ready yet, play silence bytecnt = MIN(sizeof(buf), frame_count * frame_size); memset(buf, 0, bytecnt); } snd_pcm_writei(s->ph, buf, bytecnt / frame_size); } } if (events & PA_IO_EVENT_INPUT) { if (paused) { // client stream is corked. Read data from ALSA and discard them size_t bytecnt = MIN(sizeof(buf), frame_count * frame_size); snd_pcm_readi(s->ph, buf, bytecnt / frame_size); } else { size_t bytecnt = ringbuffer_writable_size(s->rb); if (bytecnt == 0) { // ringbuffer is full because app doesn't read data fast enough. // Make some room ringbuffer_drop(s->rb, frame_count * frame_size); bytecnt = ringbuffer_writable_size(s->rb); } bytecnt = MIN(bytecnt, frame_count * frame_size); bytecnt = MIN(bytecnt, sizeof(buf)); if (bytecnt > 0) { snd_pcm_readi(s->ph, buf, bytecnt / frame_size); ringbuffer_write(s->rb, buf, bytecnt); } size_t readable_size = pa_stream_readable_size(s); if (s->read_cb && readable_size > 0) s->read_cb(s, readable_size, s->read_cb_userdata); } } }