diff options
author | Ricardo Biehl Pasquali <pasqualirb@gmail.com> | 2018-12-18 23:26:15 -0200 |
---|---|---|
committer | Ricardo Biehl Pasquali <pasqualirb@gmail.com> | 2019-01-08 16:48:19 -0200 |
commit | 4ee09a97af1f770241e24e4d395e28c1ad1343c5 (patch) | |
tree | f304dd079230c98591789dee91e7f711371a2321 /src | |
parent | 7aacaed904d237b2ae737a8bc8104101585446cb (diff) |
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 <pasqualirb@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/pcm.c | 125 |
1 files changed, 67 insertions, 58 deletions
@@ -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. |