From 13bf6299d4f6907bcdc97729a17c6172d46637c9 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Wed, 22 Aug 2018 16:10:45 -0300 Subject: pcm: Do prepare action in pcm_open() This eliminates the need for prepare in transfer functions and pcm_start(). Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pcm.c b/src/pcm.c index 96a5788..f854a94 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1129,6 +1129,10 @@ struct pcm *pcm_open(unsigned int card, unsigned int device, } #endif + /* prepare here so the user does not need to do this later */ + if (pcm_prepare(pcm)) + goto fail; + pcm->underruns = 0; return pcm; -- cgit v1.2.3 From 7c40a9ecde0150049bf1b04a9fbf0fd437c16043 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Sun, 9 Sep 2018 21:40:01 -0300 Subject: pcm: Move prepare action to xrun handling Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index f854a94..43e8279 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -704,9 +704,6 @@ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) x.result = 0; for (;;) { if (!pcm->running) { - int prepare_error = pcm_prepare(pcm); - if (prepare_error) - return prepare_error; if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) return oops(pcm, errno, "cannot write initial data"); pcm->running = 1; @@ -722,6 +719,8 @@ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) pcm->underruns++; if (pcm->flags & PCM_NORESTART) return -EPIPE; + if (pcm_prepare(pcm)) + return -EPIPE; continue; } return oops(pcm, errno, "cannot write stream data"); -- cgit v1.2.3 From 5f247ef8d5ef39922dde2e2f12c7a2ab7393d34b Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Mon, 20 Aug 2018 19:46:56 -0300 Subject: pcm: Remove code assuming start after write The state cannot be assumed as RUNNING after a write because start_threshold may not have elapsed. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index 43e8279..7dec5f7 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -703,12 +703,6 @@ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) x.frames = frame_count; x.result = 0; for (;;) { - if (!pcm->running) { - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) - return oops(pcm, errno, "cannot write initial data"); - pcm->running = 1; - return x.result; - } if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { pcm->prepared = 0; pcm->running = 0; -- cgit v1.2.3 From f85cf62763b170698a91a7dd176695b581dbe358 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Mon, 7 Jan 2019 19:33:13 -0200 Subject: pcm: Prepare after overrun Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pcm.c b/src/pcm.c index 7dec5f7..b02883f 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -760,6 +760,10 @@ int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) if (errno == EPIPE) { /* we failed to make our window -- try to restart */ pcm->underruns++; + if (pcm->flags & PCM_NORESTART) + return -EPIPE; + if (pcm_prepare(pcm)) + return -EPIPE; continue; } return oops(pcm, errno, "cannot read stream data"); -- cgit v1.2.3 From 13e11fea99849b34ba4fe2f06475c71c81fd8e59 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Tue, 21 Aug 2018 14:42:23 -0300 Subject: pcm: Remove start before read Start is done by kernel when start_threshold or more frames are requested. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index b02883f..3900ee6 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -752,9 +752,7 @@ int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) x.frames = frame_count; x.result = 0; for (;;) { - if ((!pcm->running) && (pcm_start(pcm) < 0)) - return -errno; - else if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { pcm->prepared = 0; pcm->running = 0; if (errno == EPIPE) { -- cgit v1.2.3 From 7ff9cde3fc45014c6b0457a846d5902b1fbfe5b0 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Mon, 31 Dec 2018 17:23:39 -0200 Subject: pcm: Remove prepare before start Prepare is done in pcm_open(). Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index 3900ee6..b453c6f 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1205,20 +1205,13 @@ int pcm_prepare(struct pcm *pcm) } /** Starts a PCM. - * If the PCM has not been prepared, - * it is prepared in this function. * @param pcm A PCM handle. * @return On success, zero; on failure, a negative number. * @ingroup libtinyalsa-pcm */ int pcm_start(struct pcm *pcm) { - int prepare_error = pcm_prepare(pcm); - if (prepare_error) - return prepare_error; - - /* if pcm is linked, it may be already started by other pcm */ - /* check pcm state is not in running state */ + /* set appl_ptr and avail_min in kernel */ pcm_sync_ptr(pcm, 0); if (pcm->mmap_status->state != PCM_STATE_RUNNING) { -- cgit v1.2.3 From c8eb9c98a65c63090de7354441528718f6390fc9 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Mon, 20 Aug 2018 19:14:02 -0300 Subject: pcm: Remove 'prepared' variable from pcm structure It is not needed. As the pcm structure is opaque to user it can be safely removed. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index b453c6f..f22e721 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -217,8 +217,6 @@ struct pcm { unsigned int flags; /** Whether the PCM is running or not */ int running:1; - /** Whether or not the PCM has been prepared */ - int prepared:1; /** The number of underruns that have occured */ int underruns; /** Size of the buffer */ @@ -704,7 +702,6 @@ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) x.result = 0; for (;;) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { - pcm->prepared = 0; pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart if we are @@ -753,7 +750,6 @@ int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) x.result = 0; for (;;) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { - pcm->prepared = 0; pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart */ @@ -1012,7 +1008,6 @@ int pcm_close(struct pcm *pcm) if (pcm->fd >= 0) close(pcm->fd); - pcm->prepared = 0; pcm->running = 0; pcm->buffer_size = 0; pcm->fd = -1; @@ -1194,13 +1189,9 @@ int pcm_unlink(struct pcm *pcm) */ int pcm_prepare(struct pcm *pcm) { - if (pcm->prepared) - return 0; - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0) return oops(pcm, errno, "cannot prepare channel"); - pcm->prepared = 1; return 0; } @@ -1233,7 +1224,6 @@ int pcm_stop(struct pcm *pcm) if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) return oops(pcm, errno, "cannot stop channel"); - pcm->prepared = 0; pcm->running = 0; return 0; } @@ -1431,7 +1421,6 @@ int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes) err = pcm_wait(pcm, time); if (err < 0) { - pcm->prepared = 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, -- cgit v1.2.3 From 535a6bac9a576b4d8553a295669a07c5609d4ab3 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Sun, 30 Dec 2018 11:53:54 -0200 Subject: pcm: Get appl_ptr and avail_min after prepare appl_ptr becomes zero in prepare action. avail_min is the same as when it was set in software parameters. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pcm.c b/src/pcm.c index f22e721..289b426 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1192,6 +1192,9 @@ int pcm_prepare(struct pcm *pcm) if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0) return oops(pcm, errno, "cannot prepare channel"); + /* get appl_ptr and avail_min from kernel */ + pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_AVAIL_MIN); + return 0; } -- cgit v1.2.3 From 7a512de1fb2c43212b368ad85b4d1388e38639b9 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Wed, 2 Jan 2019 17:27:52 -0200 Subject: pcm: Do not set values after control/status mmap It's not necessary to send appl_ptr or avail_min to the kernel here. In prepare action, called later in pcm_open(), appl_ptr becomes zero. avail_min is already defined in software parameters. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index 289b426..3c08cad 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -549,7 +549,6 @@ static int pcm_hw_mmap_status(struct pcm *pcm) pcm->mmap_status = NULL; goto mmap_error; } - pcm->mmap_control->avail_min = 1; return 0; @@ -560,8 +559,6 @@ mmap_error: return -ENOMEM; pcm->mmap_status = &pcm->sync_ptr->s.status; pcm->mmap_control = &pcm->sync_ptr->c.control; - pcm->mmap_control->avail_min = 1; - pcm_sync_ptr(pcm, 0); return 0; } -- cgit v1.2.3 From 7a286f6499128bfbba8ab831cc8ffdbf82330857 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Tue, 18 Dec 2018 22:57:22 -0200 Subject: pcm: Remove HWSYNC before available calculation Hardware pointer should not be updated in available calculation. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pcm.c b/src/pcm.c index 3c08cad..8ef5a48 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1252,7 +1252,6 @@ static inline int pcm_mmap_capture_avail(struct pcm *pcm) static inline int pcm_mmap_avail(struct pcm *pcm) { - pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC); if (pcm->flags & PCM_IN) return pcm_mmap_capture_avail(pcm); else -- cgit v1.2.3 From c1446758c76a8319382aad3b56df09c45d51bba4 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Tue, 18 Dec 2018 23:13:05 -0200 Subject: pcm: Call HWSYNC ioctl when status is mmaped When hardware pointer update is requested and status structure is mmaped, call HWSYNC ioctl. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index 8ef5a48..4bc86aa 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -516,14 +516,23 @@ unsigned int pcm_frames_to_bytes(const struct pcm *pcm, unsigned int frames) static int pcm_sync_ptr(struct pcm *pcm, int flags) { - if (pcm->sync_ptr) { + if (pcm->sync_ptr == NULL) { + /* status and control are mmaped */ + + if (flags & SNDRV_PCM_SYNC_PTR_HWSYNC) { + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HWSYNC) == -1) { + oops(pcm, errno, "failed to sync hardware pointer"); + return -1; + } + } + } else { pcm->sync_ptr->flags = flags; if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0) { oops(pcm, errno, "failed to sync mmap ptr"); return -1; } - return 0; } + return 0; } -- cgit v1.2.3 From b00f90ab08db5f458aae1bc0f66690b9f0e10be4 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Sat, 22 Dec 2018 12:51:21 -0200 Subject: pcm: Get values from kernel in avail_update Instead of sending appl_ptr and avail_min to the kernel, get them. This is the behavior of alsa-lib. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcm.c b/src/pcm.c index 4bc86aa..3a8d4e9 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1325,7 +1325,7 @@ int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames) int pcm_avail_update(struct pcm *pcm) { - pcm_sync_ptr(pcm, 0); + pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_AVAIL_MIN); return pcm_mmap_avail(pcm); } -- cgit v1.2.3 From 19f84c32da55771b1ea887149c52de6d583301de Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Mon, 24 Dec 2018 12:40:31 -0200 Subject: pcm: Move pcm_get_htimestamp() pcm_get_htimestamp() will be rewritten. Place it after pcm_avail_update(), which will be used in the rewrite. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 94 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index 3a8d4e9..013c007 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -631,53 +631,6 @@ static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf, return count; } -/** Returns available frames in pcm buffer and corresponding time stamp. - * The clock is CLOCK_MONOTONIC if flag @ref PCM_MONOTONIC was specified in @ref pcm_open, - * otherwise the clock is CLOCK_REALTIME. - * For an input stream, frames available are frames ready for the application to read. - * For an output stream, frames available are the number of empty frames available for the application to write. - * Only available for PCMs opened with the @ref PCM_MMAP flag. - * @param pcm A PCM handle. - * @param avail The number of available frames - * @param tstamp The timestamp - * @return On success, zero is returned; on failure, negative one. - */ -int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, - struct timespec *tstamp) -{ - int frames; - int rc; - snd_pcm_uframes_t hw_ptr; - - if (!pcm_is_ready(pcm)) - return -1; - - rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC); - if (rc < 0) - return -1; - - if ((pcm->mmap_status->state != PCM_STATE_RUNNING) && - (pcm->mmap_status->state != PCM_STATE_DRAINING)) - return -1; - - *tstamp = pcm->mmap_status->tstamp; - if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0) - return -1; - - hw_ptr = pcm->mmap_status->hw_ptr; - if (pcm->flags & PCM_IN) - frames = hw_ptr - pcm->mmap_control->appl_ptr; - else - frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; - - if (frames < 0) - return -1; - - *avail = (unsigned int)frames; - - return 0; -} - /** Writes audio samples to PCM. * If the PCM has not been started, it is started in this function. * This function is only valid for PCMs opened with the @ref PCM_OUT flag. @@ -1329,6 +1282,53 @@ int pcm_avail_update(struct pcm *pcm) return pcm_mmap_avail(pcm); } +/** Returns available frames in pcm buffer and corresponding time stamp. + * The clock is CLOCK_MONOTONIC if flag @ref PCM_MONOTONIC was specified in @ref pcm_open, + * otherwise the clock is CLOCK_REALTIME. + * For an input stream, frames available are frames ready for the application to read. + * For an output stream, frames available are the number of empty frames available for the application to write. + * Only available for PCMs opened with the @ref PCM_MMAP flag. + * @param pcm A PCM handle. + * @param avail The number of available frames + * @param tstamp The timestamp + * @return On success, zero is returned; on failure, negative one. + */ +int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, + struct timespec *tstamp) +{ + int frames; + int rc; + snd_pcm_uframes_t hw_ptr; + + if (!pcm_is_ready(pcm)) + return -1; + + rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC); + if (rc < 0) + return -1; + + if ((pcm->mmap_status->state != PCM_STATE_RUNNING) && + (pcm->mmap_status->state != PCM_STATE_DRAINING)) + return -1; + + *tstamp = pcm->mmap_status->tstamp; + if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0) + return -1; + + hw_ptr = pcm->mmap_status->hw_ptr; + if (pcm->flags & PCM_IN) + frames = hw_ptr - pcm->mmap_control->appl_ptr; + else + frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; + + if (frames < 0) + return -1; + + *avail = (unsigned int)frames; + + return 0; +} + int pcm_state(struct pcm *pcm) { int err = pcm_sync_ptr(pcm, 0); -- cgit v1.2.3 From 7bdb05d882370333cf1e2440cd6082d1a3be6750 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Sat, 22 Dec 2018 14:42:57 -0200 Subject: pcm: Rewrite pcm_get_htimestamp() Use pcm_avail_update(), check avail for timestamp coherence. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index 013c007..d037f8d 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1287,7 +1287,6 @@ int pcm_avail_update(struct pcm *pcm) * otherwise the clock is CLOCK_REALTIME. * For an input stream, frames available are frames ready for the application to read. * For an output stream, frames available are the number of empty frames available for the application to write. - * Only available for PCMs opened with the @ref PCM_MMAP flag. * @param pcm A PCM handle. * @param avail The number of available frames * @param tstamp The timestamp @@ -1296,36 +1295,36 @@ int pcm_avail_update(struct pcm *pcm) int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, struct timespec *tstamp) { - int frames; - int rc; - snd_pcm_uframes_t hw_ptr; + int checking; + int tmp; if (!pcm_is_ready(pcm)) return -1; - rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC); - if (rc < 0) - return -1; + checking = 0; - if ((pcm->mmap_status->state != PCM_STATE_RUNNING) && - (pcm->mmap_status->state != PCM_STATE_DRAINING)) - return -1; +again: - *tstamp = pcm->mmap_status->tstamp; - if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0) - return -1; + tmp = pcm_avail_update(pcm); + if (tmp < 0) + return tmp; /* error */ - hw_ptr = pcm->mmap_status->hw_ptr; - if (pcm->flags & PCM_IN) - frames = hw_ptr - pcm->mmap_control->appl_ptr; - else - frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr; + if (checking && (unsigned int) tmp == *avail) + return 0; - if (frames < 0) - return -1; + *avail = (unsigned int) tmp; + *tstamp = pcm->mmap_status->tstamp; - *avail = (unsigned int)frames; + /* + * When status is mmaped, get avail again to ensure + * valid timestamp. + */ + if (!pcm->sync_ptr) { + checking = 1; + goto again; + } + /* SYNC_PTR ioctl was used, no need to check avail */ return 0; } -- cgit v1.2.3 From e45431eab4840a48ca62c3e5ef49fe46dac75a48 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Wed, 19 Dec 2018 23:19:57 -0200 Subject: pcm: Add macro for PREPARED state Signed-off-by: Ricardo Biehl Pasquali --- include/tinyalsa/pcm.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/tinyalsa/pcm.h b/include/tinyalsa/pcm.h index 9b22c55..46c9b94 100644 --- a/include/tinyalsa/pcm.h +++ b/include/tinyalsa/pcm.h @@ -97,6 +97,11 @@ extern "C" { * */ #define PCM_NONBLOCK 0x00000010 +/** Means a PCM is prepared + * @ingroup libtinyalsa-pcm + */ +#define PCM_STATE_PREPARED 0x02 + /** For inputs, this means the PCM is recording audio samples. * For outputs, this means the PCM is playing audio samples. * @ingroup libtinyalsa-pcm -- cgit v1.2.3 From d759f4801789c6c83174a5eca474b59eda3e8090 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Sat, 22 Dec 2018 11:17:32 -0200 Subject: pcm: Put mmap functions together pcm_mmap_transfer() will be rewritten. Put together functions related to mmap transfer. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 88 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index d037f8d..e6f3b48 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -587,50 +587,6 @@ static void pcm_hw_munmap_status(struct pcm *pcm) { pcm->mmap_control = NULL; } -static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset, - char *buf, unsigned int src_offset, - unsigned int frames) -{ - int size_bytes = pcm_frames_to_bytes(pcm, frames); - int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset); - int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset); - - /* interleaved only atm */ - if (pcm->flags & PCM_IN) - memcpy(buf + src_offset_bytes, - (char*)pcm->mmap_buffer + pcm_offset_bytes, - size_bytes); - else - memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes, - buf + src_offset_bytes, - size_bytes); - return 0; -} - -static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf, - unsigned int offset, unsigned int size) -{ - void *pcm_areas; - int commit; - unsigned int pcm_offset, frames, count = 0; - - while (size > 0) { - frames = size; - pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames); - pcm_areas_copy(pcm, pcm_offset, buf, offset, frames); - commit = pcm_mmap_commit(pcm, pcm_offset, frames); - if (commit < 0) { - oops(pcm, commit, "failed to commit %d frames\n", frames); - return commit; - } - - offset += commit; - count += commit; - size -= commit; - } - return count; -} - /** Writes audio samples to PCM. * If the PCM has not been started, it is started in this function. * This function is only valid for PCMs opened with the @ref PCM_OUT flag. @@ -1258,6 +1214,26 @@ int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, return 0; } +static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset, + char *buf, unsigned int src_offset, + unsigned int frames) +{ + int size_bytes = pcm_frames_to_bytes(pcm, frames); + int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset); + int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset); + + /* interleaved only atm */ + if (pcm->flags & PCM_IN) + memcpy(buf + src_offset_bytes, + (char*)pcm->mmap_buffer + pcm_offset_bytes, + size_bytes); + else + memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes, + buf + src_offset_bytes, + size_bytes); + return 0; +} + int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames) { int ret; @@ -1276,6 +1252,30 @@ int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames) return frames; } +static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf, + unsigned int offset, unsigned int size) +{ + void *pcm_areas; + int commit; + unsigned int pcm_offset, frames, count = 0; + + while (size > 0) { + frames = size; + pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames); + pcm_areas_copy(pcm, pcm_offset, buf, offset, frames); + commit = pcm_mmap_commit(pcm, pcm_offset, frames); + if (commit < 0) { + oops(pcm, commit, "failed to commit %d frames\n", frames); + return commit; + } + + offset += commit; + count += commit; + size -= commit; + } + return count; +} + int pcm_avail_update(struct pcm *pcm) { pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_AVAIL_MIN); -- cgit v1.2.3 From 7aacaed904d237b2ae737a8bc8104101585446cb Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Tue, 18 Dec 2018 23:24:19 -0200 Subject: pcm: Exit loop when there are no available frames Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcm.c b/src/pcm.c index e6f3b48..474295a 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1259,7 +1259,7 @@ static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf, int commit; unsigned int pcm_offset, frames, count = 0; - while (size > 0) { + while (pcm_mmap_avail(pcm) && size) { frames = size; pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames); pcm_areas_copy(pcm, pcm_offset, buf, offset, frames); -- cgit v1.2.3 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(-) 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 From 297d24b7eb264f1fad464c15e4298f714452877e Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Wed, 19 Dec 2018 14:05:05 -0200 Subject: pcm: Remove 'running' variable from pcm structure It is not needed. As the pcm structure is opaque to user it can be safely removed. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index 5ba01ca..f49be90 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -215,8 +215,6 @@ struct pcm { int fd; /** Flags that were passed to @ref pcm_open */ unsigned int flags; - /** Whether the PCM is running or not */ - int running:1; /** The number of underruns that have occured */ int underruns; /** Size of the buffer */ @@ -617,7 +615,6 @@ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) x.result = 0; for (;;) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { - pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart if we are * allowed to do so. Otherwise, simply allow the EPIPE error to @@ -665,7 +662,6 @@ int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) x.result = 0; for (;;) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { - pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart */ pcm->underruns++; @@ -923,7 +919,6 @@ int pcm_close(struct pcm *pcm) if (pcm->fd >= 0) close(pcm->fd); - pcm->running = 0; pcm->buffer_size = 0; pcm->fd = -1; free(pcm); @@ -1128,7 +1123,6 @@ int pcm_start(struct pcm *pcm) return oops(pcm, errno, "cannot start channel"); } - pcm->running = 1; return 0; } @@ -1142,7 +1136,6 @@ int pcm_stop(struct pcm *pcm) if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) return oops(pcm, errno, "cannot stop channel"); - pcm->running = 0; return 0; } -- cgit v1.2.3 From b8ea4c4b0c3da305d97bdf297520b9ace0346281 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Mon, 24 Dec 2018 18:34:48 -0200 Subject: pcm: Put transfer functions together A generic transfer function will be created. Put together functions related to it. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 246 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 122 insertions(+), 124 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index f49be90..d20a954 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -585,130 +585,6 @@ static void pcm_hw_munmap_status(struct pcm *pcm) { pcm->mmap_control = NULL; } -/** Writes audio samples to PCM. - * If the PCM has not been started, it is started in this function. - * This function is only valid for PCMs opened with the @ref PCM_OUT flag. - * This function is not valid for PCMs opened with the @ref PCM_MMAP flag. - * @param pcm A PCM handle. - * @param data The audio sample array - * @param frame_count The number of frames occupied by the sample array. - * This value should not be greater than @ref TINYALSA_FRAMES_MAX - * or INT_MAX. - * @return On success, this function returns the number of frames written; otherwise, a negative number. - * @ingroup libtinyalsa-pcm - */ -int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) -{ - struct snd_xferi x; - - if (pcm->flags & PCM_IN) - return -EINVAL; -#if UINT_MAX > TINYALSA_FRAMES_MAX - if (frame_count > TINYALSA_FRAMES_MAX) - return -EINVAL; -#endif - if (frame_count > INT_MAX) - return -EINVAL; - - x.buf = (void*)data; - x.frames = frame_count; - x.result = 0; - for (;;) { - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { - if (errno == EPIPE) { - /* we failed to make our window -- try to restart if we are - * allowed to do so. Otherwise, simply allow the EPIPE error to - * propagate up to the app level */ - pcm->underruns++; - if (pcm->flags & PCM_NORESTART) - return -EPIPE; - if (pcm_prepare(pcm)) - return -EPIPE; - continue; - } - return oops(pcm, errno, "cannot write stream data"); - } - return x.result; - } -} - -/** Reads audio samples from PCM. - * If the PCM has not been started, it is started in this function. - * This function is only valid for PCMs opened with the @ref PCM_IN flag. - * This function is not valid for PCMs opened with the @ref PCM_MMAP flag. - * @param pcm A PCM handle. - * @param data The audio sample array - * @param frame_count The number of frames occupied by the sample array. - * This value should not be greater than @ref TINYALSA_FRAMES_MAX - * or INT_MAX. - * @return On success, this function returns the number of frames written; otherwise, a negative number. - * @ingroup libtinyalsa-pcm - */ -int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) -{ - struct snd_xferi x; - - if (!(pcm->flags & PCM_IN)) - return -EINVAL; -#if UINT_MAX > TINYALSA_FRAMES_MAX - if (frame_count > TINYALSA_FRAMES_MAX) - return -EINVAL; -#endif - if (frame_count > INT_MAX) - return -EINVAL; - - x.buf = data; - x.frames = frame_count; - x.result = 0; - for (;;) { - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { - if (errno == EPIPE) { - /* we failed to make our window -- try to restart */ - pcm->underruns++; - if (pcm->flags & PCM_NORESTART) - return -EPIPE; - if (pcm_prepare(pcm)) - return -EPIPE; - continue; - } - return oops(pcm, errno, "cannot read stream data"); - } - return x.result; - } -} - -/** Writes audio samples to PCM. - * If the PCM has not been started, it is started in this function. - * This function is only valid for PCMs opened with the @ref PCM_OUT flag. - * This function is not valid for PCMs opened with the @ref PCM_MMAP flag. - * @param pcm A PCM handle. - * @param data The audio sample array - * @param count The number of bytes occupied by the sample array. - * @return On success, this function returns zero; otherwise, a negative number. - * @deprecated - * @ingroup libtinyalsa-pcm - */ -int pcm_write(struct pcm *pcm, const void *data, unsigned int count) -{ - return pcm_writei(pcm, data, pcm_bytes_to_frames(pcm, count)); -} - -/** Reads audio samples from PCM. - * If the PCM has not been started, it is started in this function. - * This function is only valid for PCMs opened with the @ref PCM_IN flag. - * This function is not valid for PCMs opened with the @ref PCM_MMAP flag. - * @param pcm A PCM handle. - * @param data The audio sample array - * @param count The number of bytes occupied by the sample array. - * @return On success, this function returns zero; otherwise, a negative number. - * @deprecated - * @ingroup libtinyalsa-pcm - */ -int pcm_read(struct pcm *pcm, void *data, unsigned int count) -{ - return pcm_readi(pcm, data, pcm_bytes_to_frames(pcm, count)); -} - static struct pcm bad_pcm = { .fd = -1, }; @@ -1481,6 +1357,128 @@ int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) return pcm_mmap_transfer(pcm, data, pcm_bytes_to_frames(pcm, count)); } +/** Writes audio samples to PCM. + * If the PCM has not been started, it is started in this function. + * This function is only valid for PCMs opened with the @ref PCM_OUT flag. + * @param pcm A PCM handle. + * @param data The audio sample array + * @param frame_count The number of frames occupied by the sample array. + * This value should not be greater than @ref TINYALSA_FRAMES_MAX + * or INT_MAX. + * @return On success, this function returns the number of frames written; otherwise, a negative number. + * @ingroup libtinyalsa-pcm + */ +int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) +{ + struct snd_xferi x; + + if (pcm->flags & PCM_IN) + return -EINVAL; +#if UINT_MAX > TINYALSA_FRAMES_MAX + if (frame_count > TINYALSA_FRAMES_MAX) + return -EINVAL; +#endif + if (frame_count > INT_MAX) + return -EINVAL; + + x.buf = (void*)data; + x.frames = frame_count; + x.result = 0; + for (;;) { + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { + if (errno == EPIPE) { + /* we failed to make our window -- try to restart if we are + * allowed to do so. Otherwise, simply allow the EPIPE error to + * propagate up to the app level */ + pcm->underruns++; + if (pcm->flags & PCM_NORESTART) + return -EPIPE; + if (pcm_prepare(pcm)) + return -EPIPE; + continue; + } + return oops(pcm, errno, "cannot write stream data"); + } + return x.result; + } +} + +/** Reads audio samples from PCM. + * If the PCM has not been started, it is started in this function. + * This function is only valid for PCMs opened with the @ref PCM_IN flag. + * @param pcm A PCM handle. + * @param data The audio sample array + * @param frame_count The number of frames occupied by the sample array. + * This value should not be greater than @ref TINYALSA_FRAMES_MAX + * or INT_MAX. + * @return On success, this function returns the number of frames written; otherwise, a negative number. + * @ingroup libtinyalsa-pcm + */ +int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) +{ + struct snd_xferi x; + + if (!(pcm->flags & PCM_IN)) + return -EINVAL; +#if UINT_MAX > TINYALSA_FRAMES_MAX + if (frame_count > TINYALSA_FRAMES_MAX) + return -EINVAL; +#endif + if (frame_count > INT_MAX) + return -EINVAL; + + x.buf = data; + x.frames = frame_count; + x.result = 0; + for (;;) { + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { + if (errno == EPIPE) { + /* we failed to make our window -- try to restart */ + pcm->underruns++; + if (pcm->flags & PCM_NORESTART) + return -EPIPE; + if (pcm_prepare(pcm)) + return -EPIPE; + continue; + } + return oops(pcm, errno, "cannot read stream data"); + } + return x.result; + } +} + +/** Writes audio samples to PCM. + * If the PCM has not been started, it is started in this function. + * This function is only valid for PCMs opened with the @ref PCM_OUT flag. + * This function is not valid for PCMs opened with the @ref PCM_MMAP flag. + * @param pcm A PCM handle. + * @param data The audio sample array + * @param count The number of bytes occupied by the sample array. + * @return On success, this function returns zero; otherwise, a negative number. + * @deprecated + * @ingroup libtinyalsa-pcm + */ +int pcm_write(struct pcm *pcm, const void *data, unsigned int count) +{ + return pcm_writei(pcm, data, pcm_bytes_to_frames(pcm, count)); +} + +/** Reads audio samples from PCM. + * If the PCM has not been started, it is started in this function. + * This function is only valid for PCMs opened with the @ref PCM_IN flag. + * This function is not valid for PCMs opened with the @ref PCM_MMAP flag. + * @param pcm A PCM handle. + * @param data The audio sample array + * @param count The number of bytes occupied by the sample array. + * @return On success, this function returns zero; otherwise, a negative number. + * @deprecated + * @ingroup libtinyalsa-pcm + */ +int pcm_read(struct pcm *pcm, void *data, unsigned int count) +{ + return pcm_readi(pcm, data, pcm_bytes_to_frames(pcm, count)); +} + /** Gets the delay of the PCM, in terms of frames. * @param pcm A PCM handle. * @returns On success, the delay of the PCM. -- cgit v1.2.3 From 03cc7c5cb7e9ea227af43cedf58dc60febb7b745 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Fri, 4 Jan 2019 00:20:14 -0200 Subject: pcm: Create generic transfer Group read/write transfer into pcm_rw_transfer(). Create pcm_generic_transfer() for read/write and mmap transfer modes. This recovers from xrun and suspended. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 120 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 54 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index d20a954..eb2f4b8 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1357,6 +1357,70 @@ int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) return pcm_mmap_transfer(pcm, data, pcm_bytes_to_frames(pcm, count)); } +static int pcm_rw_transfer(struct pcm *pcm, void *data, unsigned int frames) +{ + int is_playback; + + struct snd_xferi transfer; + int res; + + is_playback = !(pcm->flags & PCM_IN); + + transfer.buf = data; + transfer.frames = frames; + transfer.result = 0; + + res = ioctl(pcm->fd, is_playback + ? SNDRV_PCM_IOCTL_WRITEI_FRAMES + : SNDRV_PCM_IOCTL_READI_FRAMES, &transfer); + + return res == 0 ? (int) transfer.result : -1; +} + +static int pcm_generic_transfer(struct pcm *pcm, void *data, + unsigned int frames) +{ + int res; + +#if UINT_MAX > TINYALSA_FRAMES_MAX + if (frames > TINYALSA_FRAMES_MAX) + return -EINVAL; +#endif + if (frames > INT_MAX) + return -EINVAL; + +again: + + if (pcm->flags & PCM_MMAP) + res = pcm_mmap_transfer(pcm, data, frames); + else + res = pcm_rw_transfer(pcm, data, frames); + + if (res < 0) { + switch (errno) { + case EPIPE: + pcm->underruns++; + /* fallthrough */ + case ESTRPIPE: + /* + * Try to restart if we are allowed to do so. + * Otherwise, return error. + */ + if (pcm->flags & PCM_NORESTART || pcm_prepare(pcm)) + return -1; + goto again; + case EAGAIN: + if (pcm->flags & PCM_NONBLOCK) + return -1; + /* fallthrough */ + default: + return oops(pcm, errno, "cannot read/write stream data"); + } + } + + return res; +} + /** Writes audio samples to PCM. * If the PCM has not been started, it is started in this function. * This function is only valid for PCMs opened with the @ref PCM_OUT flag. @@ -1370,37 +1434,10 @@ int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) */ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) { - struct snd_xferi x; - if (pcm->flags & PCM_IN) return -EINVAL; -#if UINT_MAX > TINYALSA_FRAMES_MAX - if (frame_count > TINYALSA_FRAMES_MAX) - return -EINVAL; -#endif - if (frame_count > INT_MAX) - return -EINVAL; - x.buf = (void*)data; - x.frames = frame_count; - x.result = 0; - for (;;) { - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { - if (errno == EPIPE) { - /* we failed to make our window -- try to restart if we are - * allowed to do so. Otherwise, simply allow the EPIPE error to - * propagate up to the app level */ - pcm->underruns++; - if (pcm->flags & PCM_NORESTART) - return -EPIPE; - if (pcm_prepare(pcm)) - return -EPIPE; - continue; - } - return oops(pcm, errno, "cannot write stream data"); - } - return x.result; - } + return pcm_generic_transfer(pcm, (void*) data, frame_count); } /** Reads audio samples from PCM. @@ -1416,35 +1453,10 @@ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) */ int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) { - struct snd_xferi x; - if (!(pcm->flags & PCM_IN)) return -EINVAL; -#if UINT_MAX > TINYALSA_FRAMES_MAX - if (frame_count > TINYALSA_FRAMES_MAX) - return -EINVAL; -#endif - if (frame_count > INT_MAX) - return -EINVAL; - x.buf = data; - x.frames = frame_count; - x.result = 0; - for (;;) { - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { - if (errno == EPIPE) { - /* we failed to make our window -- try to restart */ - pcm->underruns++; - if (pcm->flags & PCM_NORESTART) - return -EPIPE; - if (pcm_prepare(pcm)) - return -EPIPE; - continue; - } - return oops(pcm, errno, "cannot read stream data"); - } - return x.result; - } + return pcm_generic_transfer(pcm, data, frame_count); } /** Writes audio samples to PCM. -- cgit v1.2.3 From 1804d158ba92fb251a0a6a6f38b0d5082d7c91ae Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Fri, 4 Jan 2019 00:32:40 -0200 Subject: pcm: Replace 'underruns' with 'xruns' Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pcm.c b/src/pcm.c index eb2f4b8..cec4e47 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -215,8 +215,8 @@ struct pcm { int fd; /** Flags that were passed to @ref pcm_open */ unsigned int flags; - /** The number of underruns that have occured */ - int underruns; + /** The number of (under/over)runs that have occured */ + int xruns; /** Size of the buffer */ unsigned int buffer_size; /** The boundary for ring buffer pointers */ @@ -909,7 +909,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device, if (pcm_prepare(pcm)) goto fail; - pcm->underruns = 0; + pcm->xruns = 0; return pcm; fail: @@ -1399,7 +1399,7 @@ again: if (res < 0) { switch (errno) { case EPIPE: - pcm->underruns++; + pcm->xruns++; /* fallthrough */ case ESTRPIPE: /* -- cgit v1.2.3 From 0bfac896e66548130c763d015ac16347ced95b84 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Fri, 4 Jan 2019 14:45:35 -0200 Subject: pcm: Cleanup control/status when setup fails Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pcm.c b/src/pcm.c index cec4e47..ac454b0 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -913,6 +913,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device, return pcm; fail: + pcm_hw_munmap_status(pcm); if (flags & PCM_MMAP) munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size)); fail_close: -- cgit v1.2.3 From b38b6a670c359e497926d799cea4d9c80d561eab Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Mon, 7 Jan 2019 11:54:47 -0200 Subject: pcm: Check for error after setting appl_ptr Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pcm.c b/src/pcm.c index ac454b0..3a9deec 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -993,7 +993,8 @@ int pcm_prepare(struct pcm *pcm) int pcm_start(struct pcm *pcm) { /* set appl_ptr and avail_min in kernel */ - pcm_sync_ptr(pcm, 0); + if (pcm_sync_ptr(pcm, 0) < 0) + return -1; if (pcm->mmap_status->state != PCM_STATE_RUNNING) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0) -- cgit v1.2.3 From bc86b6b288bfaac9fc15392fcc43b8e5789a7f15 Mon Sep 17 00:00:00 2001 From: Taylor Holberton Date: Sun, 13 Jan 2019 13:10:47 -0500 Subject: Added the 'attributes.h' header --- CMakeLists.txt | 1 + Makefile | 1 + include/tinyalsa/attributes.h | 39 +++++++++++++++++++++++++++++++++++++++ include/tinyalsa/pcm.h | 28 ++++++++++------------------ 4 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 include/tinyalsa/attributes.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c78dbf..cb31c58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.0.2) project("TinyALSA" C) set (HDRS + "include/tinyalsa/attributes.h" "include/tinyalsa/version.h" "include/tinyalsa/asoundlib.h" "include/tinyalsa/pcm.h" diff --git a/Makefile b/Makefile index 7042a3c..e3ed0ef 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ clean: .PHONY: install install: install -d $(DESTDIR)$(INCDIR)/ + install include/tinyalsa/attributes.h $(DESTDIR)$(INCDIR)/ install include/tinyalsa/pcm.h $(DESTDIR)$(INCDIR)/ install include/tinyalsa/mixer.h $(DESTDIR)$(INCDIR)/ install include/tinyalsa/asoundlib.h $(DESTDIR)$(INCDIR)/ diff --git a/include/tinyalsa/attributes.h b/include/tinyalsa/attributes.h new file mode 100644 index 0000000..e33f46a --- /dev/null +++ b/include/tinyalsa/attributes.h @@ -0,0 +1,39 @@ +#ifndef TINYALSA_ATTRIBUTES_H +#define TINYALSA_ATTRIBUTES_H + +/** @defgroup libtinyalsa-attributes + * @brief GCC attributes to issue diagnostics + * when the library is being used incorrectly. + * */ + +#ifdef __GNUC__ + +/** Issues a warning when a function is being + * used that is now deprecated. + * @ingroup libtinyalsa-attributes + * */ +#define TINYALSA_DEPRECATED __attribute__((deprecated)) + +/** Issues a warning when a return code of + * a function is not checked. + * @ingroup libtinyalsa-attributes + * */ +#define TINYALSA_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) + +#else /* __GNUC__ */ + +/** This is just a placeholder for compilers + * that aren't GCC or Clang. + * @ingroup libtinyalsa-attributes + * */ +#define TINYALSA_DEPRECATED + +/** This is just a placeholder for compilers + * that aren't GCC or Clang. + * @ingroup libtinyalsa-attributes + * */ +#define TINYALSA_WARN_UNUSED_RESULT + +#endif /* __GNUC__ */ + +#endif /* TINYALSA_ATTRIBUTES_H */ diff --git a/include/tinyalsa/pcm.h b/include/tinyalsa/pcm.h index 46c9b94..36aaf85 100644 --- a/include/tinyalsa/pcm.h +++ b/include/tinyalsa/pcm.h @@ -35,13 +35,11 @@ #ifndef TINYALSA_PCM_H #define TINYALSA_PCM_H +#include + #include #include -#if defined(__cplusplus) -extern "C" { -#endif - /** A flag that specifies that the PCM is an output. * May not be bitwise AND'd with @ref PCM_IN. * Used in @ref pcm_open. @@ -128,6 +126,10 @@ extern "C" { */ #define PCM_STATE_DISCONNECTED 0x08 +#if defined(__cplusplus) +extern "C" { +#endif + /** Audio sample format of a PCM. * The first letter specifiers whether the sample is signed or unsigned. * The letter 'S' means signed. The letter 'U' means unsigned. @@ -284,23 +286,13 @@ int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, struct timespec *ts unsigned int pcm_get_subdevice(const struct pcm *pcm); -int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count); - -int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count); - -#ifdef __GNUC__ - -int pcm_write(struct pcm *pcm, const void *data, unsigned int count) __attribute((deprecated)); +int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) TINYALSA_WARN_UNUSED_RESULT; -int pcm_read(struct pcm *pcm, void *data, unsigned int count) __attribute((deprecated)); +int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) TINYALSA_WARN_UNUSED_RESULT; -#else +int pcm_write(struct pcm *pcm, const void *data, unsigned int count) TINYALSA_DEPRECATED; -int pcm_write(struct pcm *pcm, const void *data, unsigned int count); - -int pcm_read(struct pcm *pcm, void *data, unsigned int count); - -#endif +int pcm_read(struct pcm *pcm, void *data, unsigned int count) TINYALSA_DEPRECATED; int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count); -- cgit v1.2.3