From 4ee09a97af1f770241e24e4d395e28c1ad1343c5 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Tue, 18 Dec 2018 23:26:15 -0200 Subject: pcm: Rewrite pcm_mmap_transfer() for compatibility Make pcm_mmap_transfer() compatible with read/write IO. start_threshold check was not valid for capture. pcm->running checks were removed. NOIRQ fallback was removed. Clocks of sound devices may differ from the system, leading to desynchronization. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 125 +++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/pcm.c b/src/pcm.c index 474295a..5ba01ca 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1386,81 +1386,89 @@ int pcm_wait(struct pcm *pcm, int timeout) return 1; } -int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes) +/* + * Transfer data to/from mmaped buffer. This imitates the + * behavior of read/write system calls. + * + * However, this doesn't seems to offer any advantage over + * the read/write syscalls. Should it be removed? + */ +int pcm_mmap_transfer(struct pcm *pcm, void *buffer, unsigned int frames) { - int err = 0, frames, avail; - unsigned int offset = 0, count; + int is_playback; - if (bytes == 0) - return 0; + int state; + unsigned int avail; + unsigned int user_offset; - count = pcm_bytes_to_frames(pcm, bytes); + int err; + int tmp; - while (count > 0) { + is_playback = !(pcm->flags & PCM_IN); - /* get the available space for writing new frames */ - avail = pcm_avail_update(pcm); - if (avail < 0) { - fprintf(stderr, "cannot determine available mmap frames"); - return err; - } + if (frames == 0) + return 0; - /* start the audio if we reach the threshold */ - if (!pcm->running && - (pcm->buffer_size - avail) >= pcm->config.start_threshold) { - if (pcm_start(pcm) < 0) { - fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n", - (unsigned int)pcm->mmap_status->hw_ptr, - (unsigned int)pcm->mmap_control->appl_ptr, - avail); - return -errno; - } + /* update hardware pointer and get state */ + err = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC | + SNDRV_PCM_SYNC_PTR_APPL | + SNDRV_PCM_SYNC_PTR_AVAIL_MIN); + if (err == -1) + return -1; + state = pcm->mmap_status->state; + + /* start capture if frames >= threshold */ + if (!is_playback && state == PCM_STATE_PREPARED) { + if (frames >= pcm->config.start_threshold) { + err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_START); + if (err == -1) + return -1; + /* state = PCM_STATE_RUNNING */ + } else { + /* nothing to do */ + return 0; } + } - /* sleep until we have space to write new frames */ - if (pcm->running && - (unsigned int)avail < pcm->mmap_control->avail_min) { - int time = -1; + avail = pcm_mmap_avail(pcm); + user_offset = 0; - if (pcm->flags & PCM_NOIRQ) - time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min) - / pcm->noirq_frames_per_msec; + while (frames) { + if (!avail) { + if (pcm->flags & PCM_NONBLOCK) { + errno = EAGAIN; + break; + } - err = pcm_wait(pcm, time); + /* wait for interrupt */ + err = pcm_wait(pcm, -1); if (err < 0) { - pcm->running = 0; - fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n", - (unsigned int)pcm->mmap_status->hw_ptr, - (unsigned int)pcm->mmap_control->appl_ptr, - avail); - pcm->mmap_control->appl_ptr = 0; - return err; + errno = -err; + break; } - continue; - } - frames = count; - if (frames > avail) - frames = avail; + /* get hardware pointer */ + avail = pcm_avail_update(pcm); + } - if (!frames) + tmp = pcm_mmap_transfer_areas(pcm, buffer, user_offset, frames); + if (tmp < 0) break; - /* copy frames from buffer */ - frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames); - if (frames < 0) { - fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n", - (unsigned int)pcm->mmap_status->hw_ptr, - (unsigned int)pcm->mmap_control->appl_ptr, - avail); - return frames; - } + user_offset += tmp; + frames -= tmp; + avail -= tmp; - offset += frames; - count -= frames; + /* start playback if written >= start_threshold */ + if (is_playback && state == PCM_STATE_PREPARED && + pcm->buffer_size - avail >= pcm->config.start_threshold) { + err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_START); + if (err == -1) + break; + } } - return 0; + return user_offset ? (int) user_offset : -1; } int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count) @@ -1468,7 +1476,8 @@ int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count) if ((~pcm->flags) & (PCM_OUT | PCM_MMAP)) return -ENOSYS; - return pcm_mmap_transfer(pcm, (void *)data, count); + return pcm_mmap_transfer(pcm, (void *)data, + pcm_bytes_to_frames(pcm, count)); } int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) @@ -1476,7 +1485,7 @@ int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) if ((~pcm->flags) & (PCM_IN | PCM_MMAP)) return -ENOSYS; - return pcm_mmap_transfer(pcm, data, count); + return pcm_mmap_transfer(pcm, data, pcm_bytes_to_frames(pcm, count)); } /** Gets the delay of the PCM, in terms of frames. -- cgit v1.2.3