mirror of
https://github.com/openharmony/third_party_tinyalsa.git
synced 2026-07-19 23:33:30 -04:00
Merge branch 'develop'
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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)/
|
||||
|
||||
@@ -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 */
|
||||
+15
-18
@@ -35,13 +35,11 @@
|
||||
#ifndef TINYALSA_PCM_H
|
||||
#define TINYALSA_PCM_H
|
||||
|
||||
#include <tinyalsa/attributes.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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.
|
||||
@@ -97,6 +95,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
|
||||
@@ -123,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.
|
||||
@@ -279,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_writei(struct pcm *pcm, const void *data, unsigned int frame_count) TINYALSA_WARN_UNUSED_RESULT;
|
||||
|
||||
int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count);
|
||||
int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) TINYALSA_WARN_UNUSED_RESULT;
|
||||
|
||||
#ifdef __GNUC__
|
||||
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) __attribute((deprecated));
|
||||
|
||||
int pcm_read(struct pcm *pcm, void *data, unsigned int count) __attribute((deprecated));
|
||||
|
||||
#else
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -215,12 +215,8 @@ 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;
|
||||
/** Whether or not the PCM has been prepared */
|
||||
int prepared:1;
|
||||
/** 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 */
|
||||
@@ -518,14 +514,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;
|
||||
}
|
||||
|
||||
@@ -551,7 +556,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;
|
||||
|
||||
@@ -562,8 +566,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;
|
||||
}
|
||||
@@ -583,230 +585,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;
|
||||
}
|
||||
|
||||
/** 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.
|
||||
* 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 (!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;
|
||||
return x.result;
|
||||
}
|
||||
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
|
||||
* 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;
|
||||
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 ((!pcm->running) && (pcm_start(pcm) < 0))
|
||||
return -errno;
|
||||
else 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 */
|
||||
pcm->underruns++;
|
||||
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,
|
||||
};
|
||||
@@ -1017,8 +795,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;
|
||||
free(pcm);
|
||||
@@ -1129,10 +905,15 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
|
||||
}
|
||||
#endif
|
||||
|
||||
pcm->underruns = 0;
|
||||
/* prepare here so the user does not need to do this later */
|
||||
if (pcm_prepare(pcm))
|
||||
goto fail;
|
||||
|
||||
pcm->xruns = 0;
|
||||
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:
|
||||
@@ -1195,39 +976,31 @@ 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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/** 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 */
|
||||
pcm_sync_ptr(pcm, 0);
|
||||
/* set appl_ptr and avail_min in kernel */
|
||||
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)
|
||||
return oops(pcm, errno, "cannot start channel");
|
||||
}
|
||||
|
||||
pcm->running = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1241,8 +1014,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;
|
||||
}
|
||||
|
||||
@@ -1270,7 +1041,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
|
||||
@@ -1315,6 +1085,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;
|
||||
@@ -1333,12 +1123,82 @@ 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 (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);
|
||||
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, 0);
|
||||
pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
|
||||
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.
|
||||
* @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 checking;
|
||||
int tmp;
|
||||
|
||||
if (!pcm_is_ready(pcm))
|
||||
return -1;
|
||||
|
||||
checking = 0;
|
||||
|
||||
again:
|
||||
|
||||
tmp = pcm_avail_update(pcm);
|
||||
if (tmp < 0)
|
||||
return tmp; /* error */
|
||||
|
||||
if (checking && (unsigned int) tmp == *avail)
|
||||
return 0;
|
||||
|
||||
*avail = (unsigned int) tmp;
|
||||
*tstamp = pcm->mmap_status->tstamp;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
int pcm_state(struct pcm *pcm)
|
||||
{
|
||||
int err = pcm_sync_ptr(pcm, 0);
|
||||
@@ -1397,82 +1257,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)
|
||||
int state;
|
||||
unsigned int avail;
|
||||
unsigned int user_offset;
|
||||
|
||||
int err;
|
||||
int tmp;
|
||||
|
||||
is_playback = !(pcm->flags & PCM_IN);
|
||||
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
|
||||
count = pcm_bytes_to_frames(pcm, bytes);
|
||||
/* 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;
|
||||
|
||||
while (count > 0) {
|
||||
|
||||
/* 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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* sleep until we have space to write new frames */
|
||||
if (pcm->running &&
|
||||
(unsigned int)avail < pcm->mmap_control->avail_min) {
|
||||
int time = -1;
|
||||
|
||||
if (pcm->flags & PCM_NOIRQ)
|
||||
time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
|
||||
/ pcm->noirq_frames_per_msec;
|
||||
|
||||
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,
|
||||
(unsigned int)pcm->mmap_control->appl_ptr,
|
||||
avail);
|
||||
pcm->mmap_control->appl_ptr = 0;
|
||||
return err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
frames = count;
|
||||
if (frames > avail)
|
||||
frames = avail;
|
||||
|
||||
if (!frames)
|
||||
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;
|
||||
}
|
||||
|
||||
offset += frames;
|
||||
count -= frames;
|
||||
}
|
||||
|
||||
return 0;
|
||||
avail = pcm_mmap_avail(pcm);
|
||||
user_offset = 0;
|
||||
|
||||
while (frames) {
|
||||
if (!avail) {
|
||||
if (pcm->flags & PCM_NONBLOCK) {
|
||||
errno = EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* wait for interrupt */
|
||||
err = pcm_wait(pcm, -1);
|
||||
if (err < 0) {
|
||||
errno = -err;
|
||||
break;
|
||||
}
|
||||
|
||||
/* get hardware pointer */
|
||||
avail = pcm_avail_update(pcm);
|
||||
}
|
||||
|
||||
tmp = pcm_mmap_transfer_areas(pcm, buffer, user_offset, frames);
|
||||
if (tmp < 0)
|
||||
break;
|
||||
|
||||
user_offset += tmp;
|
||||
frames -= tmp;
|
||||
avail -= tmp;
|
||||
|
||||
/* 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 user_offset ? (int) user_offset : -1;
|
||||
}
|
||||
|
||||
int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
|
||||
@@ -1480,7 +1347,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)
|
||||
@@ -1488,7 +1356,141 @@ 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));
|
||||
}
|
||||
|
||||
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->xruns++;
|
||||
/* 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.
|
||||
* @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)
|
||||
{
|
||||
if (pcm->flags & PCM_IN)
|
||||
return -EINVAL;
|
||||
|
||||
return pcm_generic_transfer(pcm, (void*) data, frame_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.
|
||||
* @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)
|
||||
{
|
||||
if (!(pcm->flags & PCM_IN))
|
||||
return -EINVAL;
|
||||
|
||||
return pcm_generic_transfer(pcm, data, frame_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.
|
||||
* 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.
|
||||
|
||||
Reference in New Issue
Block a user