/* locked version */ static int __snd_pcm_dmix_drain(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_uframes_t stop_threshold; int err; switch (snd_pcm_state(dmix->spcm)) { case SND_PCM_STATE_SUSPENDED: return -ESTRPIPE; default: break; } if (dmix->state == SND_PCM_STATE_OPEN) return -EBADFD; if (pcm->mode & SND_PCM_NONBLOCK) return -EAGAIN; if (dmix->state == SND_PCM_STATE_PREPARED) { if (snd_pcm_mmap_playback_hw_avail(pcm) > 0) snd_pcm_dmix_start(pcm); else { snd_pcm_dmix_drop(pcm); return 0; } } if (dmix->state == SND_PCM_STATE_XRUN) { snd_pcm_dmix_drop(pcm); return 0; } stop_threshold = pcm->stop_threshold; if (pcm->stop_threshold > pcm->buffer_size) pcm->stop_threshold = pcm->buffer_size; dmix->state = SND_PCM_STATE_DRAINING; do { err = snd_pcm_dmix_sync_ptr(pcm); if (err < 0) { snd_pcm_dmix_drop(pcm); return err; } if (dmix->state == SND_PCM_STATE_DRAINING) { snd_pcm_dmix_sync_area(pcm); snd_pcm_wait_nocheck(pcm, -1); snd_pcm_direct_clear_timer_queue(dmix); /* force poll to wait */ switch (snd_pcm_state(dmix->spcm)) { case SND_PCM_STATE_SUSPENDED: return -ESTRPIPE; default: break; } } } while (dmix->state == SND_PCM_STATE_DRAINING); pcm->stop_threshold = stop_threshold; 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; }
/* * 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_dshare_drain(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; snd_pcm_uframes_t stop_threshold; int err; if (dshare->state == SND_PCM_STATE_OPEN) return -EBADFD; if (pcm->mode & SND_PCM_NONBLOCK) return -EAGAIN; if (dshare->state == SND_PCM_STATE_PREPARED) { if (snd_pcm_mmap_playback_hw_avail(pcm) > 0) snd_pcm_dshare_start(pcm); else { snd_pcm_dshare_drop(pcm); return 0; } } if (dshare->state == SND_PCM_STATE_XRUN) { snd_pcm_dshare_drop(pcm); return 0; } stop_threshold = pcm->stop_threshold; if (pcm->stop_threshold > pcm->buffer_size) pcm->stop_threshold = pcm->buffer_size; dshare->state = SND_PCM_STATE_DRAINING; do { err = snd_pcm_dshare_sync_ptr(pcm); if (err < 0) { snd_pcm_dshare_drop(pcm); break; } if (dshare->state == SND_PCM_STATE_DRAINING) { snd_pcm_dshare_sync_area(pcm); snd_pcm_wait_nocheck(pcm, -1); snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */ } } while (dshare->state == SND_PCM_STATE_DRAINING); pcm->stop_threshold = stop_threshold; return 0; }
/* * synchronize hardware pointer (hw_ptr) with ours */ static int snd_pcm_dshare_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr) { snd_pcm_direct_t *dshare = pcm->private_data; snd_pcm_uframes_t old_slave_hw_ptr, avail; snd_pcm_sframes_t diff; old_slave_hw_ptr = dshare->slave_hw_ptr; dshare->slave_hw_ptr = slave_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) { snd_timer_stop(dshare->timer); do_silence(pcm); gettimestamp(&dshare->trigger_tstamp, pcm->tstamp_type); 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; }