aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/tinyalsa/pcm.h182
-rw-r--r--src/pcm.c149
2 files changed, 269 insertions, 62 deletions
diff --git a/include/tinyalsa/pcm.h b/include/tinyalsa/pcm.h
index cfc2f1d..0b02e9d 100644
--- a/include/tinyalsa/pcm.h
+++ b/include/tinyalsa/pcm.h
@@ -26,6 +26,12 @@
** DAMAGE.
*/
+/** @file */
+
+/** @defgroup tinyalsa-pcm PCM Interface
+ * @brief All macros, structures and functions that make up the PCM interface.
+ */
+
#ifndef TINYALSA_PCM_H
#define TINYALSA_PCM_H
@@ -36,61 +42,133 @@
extern "C" {
#endif
-/*
- * PCM API
+struct pcm;
+
+/** 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.
+ * @ingroup tinyalsa-pcm
*/
+#define PCM_OUT 0x00000000
-struct pcm;
+/** Specifies that the PCM is an input.
+ * May not be bitwise AND'd with @ref PCM_OUT.
+ * Used in @ref pcm_open.
+ * @ingroup tinyalsa-pcm
+ */
+#define PCM_IN 0x10000000
+
+/** Specifies that the PCM will use mmap read and write methods.
+ * Used in @ref pcm_open.
+ * @ingroup tinyalsa-pcm
+ */
+#define PCM_MMAP 0x00000001
+
+/** Specifies no interrupt requests.
+ * May only be bitwise AND'd with @ref PCM_MMAP.
+ * Used in @ref pcm_open.
+ * @ingroup tinyalsa-pcm
+ */
+#define PCM_NOIRQ 0x00000002
+
+/* When set, calls to @ref pcm_write
+ * for a playback stream will not attempt
+ * to restart the stream in the case of an
+ * underflow, but will return -EPIPE instead.
+ * After the first -EPIPE error, the stream
+ * is considered to be stopped, and a second
+ * call to pcm_write will attempt to restart
+ * the stream.
+ * Used in @ref pcm_open.
+ * @ingroup tinyalsa-pcm
+ */
+#define PCM_NORESTART 0x00000004
+
+/** Specifies monotonic timestamps.
+ * Used in @ref pcm_open.
+ * @ingroup tinyalsa-pcm
+ */
+#define PCM_MONOTONIC 0x00000008
+
+/** For inputs, this means the PCM is recording audio samples.
+ * For outputs, this means the PCM is playing audio samples.
+ * @ingroup tinyalsa-pcm
+ */
+#define PCM_STATE_RUNNING 0x03
-#define PCM_OUT 0x00000000
-#define PCM_IN 0x10000000
-#define PCM_MMAP 0x00000001
-#define PCM_NOIRQ 0x00000002
-#define PCM_NORESTART 0x00000004 /* PCM_NORESTART - when set, calls to
- * pcm_write for a playback stream will not
- * attempt to restart the stream in the case
- * of an underflow, but will return -EPIPE
- * instead. After the first -EPIPE error, the
- * stream is considered to be stopped, and a
- * second call to pcm_write will attempt to
- * restart the stream.
- */
-#define PCM_MONOTONIC 0x00000008 /* see pcm_get_htimestamp */
-
-/* PCM runtime states */
-#define PCM_STATE_RUNNING 3
-#define PCM_STATE_XRUN 4
-#define PCM_STATE_DRAINING 5
-#define PCM_STATE_SUSPENDED 7
-#define PCM_STATE_DISCONNECTED 8
-
-/* Bit formats */
+/** For inputs, this means an underrun occured.
+ * For outputs, this means an overrun occured.
+ */
+#define PCM_STATE_XRUN 0x04
+
+/** For outputs, this means audio samples are played.
+ * A PCM is in a draining state when it is coming to a stop.
+ */
+#define PCM_STATE_DRAINING 0x05
+
+/** Means a PCM is suspended.
+ * @ingroup tinyalsa-pcm
+ */
+#define PCM_STATE_SUSPENDED 0x07
+
+/** Means a PCM has been disconnected.
+ * @ingroup tinyalsa-pcm
+ */
+#define PCM_STATE_DISCONNECTED 0x08
+
+/** 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.
+ * The following number is the amount of bits that the sample occupies in memory.
+ * Following the underscore, specifiers whether the sample is big endian or little endian.
+ * The letters 'LE' mean little endian.
+ * The letters 'BE' mean big endian.
+ * This enumeration is used in the @ref config structure.
+ * @ingroup tinyalsa-pcm
+ */
enum pcm_format {
+ /** Signed, 8-bit */
PCM_FORMAT_S8 = 1,
+ /** Signed 16-bit, little endian */
PCM_FORMAT_S16_LE = 0,
+ /** Signed, 16-bit, big endian */
PCM_FORMAT_S16_BE = 2,
+ /** Signed, 24-bit (32-bit in memory), little endian */
PCM_FORMAT_S24_LE,
+ /** Signed, 24-bit (32-bit in memory), big endian */
PCM_FORMAT_S24_BE,
+ /** Signed, 24-bit, little endian */
PCM_FORMAT_S24_3LE,
+ /** Signed, 24-bit, big endian */
PCM_FORMAT_S24_3BE,
+ /** Signed, 32-bit, little endian */
PCM_FORMAT_S32_LE,
+ /** Signed, 32-bit, big endian */
PCM_FORMAT_S32_BE,
+ /** Max of the enumeration list, not an actual format. */
PCM_FORMAT_MAX
};
-/* Bitmask has 256 bits (32 bytes) in asound.h */
+/** A bit mask of 256 bits (32 bytes) that describes some hardware parameters of a PCM */
struct pcm_mask {
+ /** bits of the bit mask */
unsigned int bits[32 / sizeof(unsigned int)];
};
-/* Configuration for a stream */
+/** Encapsulates the hardware and software parameters of a PCM.
+ * @ingroup tinyalsa-pcm
+ */
struct pcm_config {
+ /** The number of channels in a frame */
unsigned int channels;
+ /** The number of frames per second */
unsigned int rate;
+ /** The number of frames in a period */
unsigned int period_size;
+ /** The number of periods in a PCM */
unsigned int period_count;
+ /** The sample format of a PCM */
enum pcm_format format;
-
/* Values to use for the ALSA start, stop and silence thresholds. Setting
* any one of these values to 0 will cause the default tinyalsa values to be
* used instead. Tinyalsa defaults are as follows.
@@ -104,21 +182,31 @@ struct pcm_config {
unsigned int silence_threshold;
};
-/* PCM parameters */
+/** Enumeration of a PCM's hardware parameters.
+ * Each of these parameters is either a mask or an interval.
+ * @ingroup tinyalsa-pcm
+ */
enum pcm_param
{
- /* mask parameters */
+ /** A mask that represents the type of read or write method available (e.g. interleaved, mmap). */
PCM_PARAM_ACCESS,
+ /** A mask that represents the @ref pcm_format available (e.g. @ref PCM_FORMAT_S32_LE) */
PCM_PARAM_FORMAT,
+ /** A mask that represents the subformat available */
PCM_PARAM_SUBFORMAT,
- /* interval parameters */
+ /** An interval representing the range of sample bits available (e.g. 8 to 32) */
PCM_PARAM_SAMPLE_BITS,
+ /** An interval representing the range of frame bits available (e.g. 8 to 64) */
PCM_PARAM_FRAME_BITS,
+ /** An interval representing the range of channels available (e.g. 1 to 2) */
PCM_PARAM_CHANNELS,
+ /** An interval representing the range of rates available (e.g. 44100 to 192000) */
PCM_PARAM_RATE,
PCM_PARAM_PERIOD_TIME,
+ /** The number of frames in a period */
PCM_PARAM_PERIOD_SIZE,
PCM_PARAM_PERIOD_BYTES,
+ /** The number of periods for a PCM */
PCM_PARAM_PERIODS,
PCM_PARAM_BUFFER_TIME,
PCM_PARAM_BUFFER_SIZE,
@@ -126,13 +214,11 @@ enum pcm_param
PCM_PARAM_TICK_TIME,
};
-/* Open and close a stream */
struct pcm *pcm_open(unsigned int card, unsigned int device,
unsigned int flags, struct pcm_config *config);
int pcm_close(struct pcm *pcm);
int pcm_is_ready(struct pcm *pcm);
-/* Obtain the parameters for a PCM */
struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
unsigned int flags);
void pcm_params_free(struct pcm_params *pcm_params);
@@ -144,64 +230,36 @@ unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
enum pcm_param param);
-/* Returns the file descriptor associated with the pcm */
int pcm_get_file_descriptor(struct pcm *pcm);
-/* Returns a human readable reason for the last error */
const char *pcm_get_error(struct pcm *pcm);
-/* Returns the sample size in bits for a PCM format.
- * As with ALSA formats, this is the storage size for the format, whereas the
- * format represents the number of significant bits. For example,
- * PCM_FORMAT_S24_LE uses 32 bits of storage.
- */
unsigned int pcm_format_to_bits(enum pcm_format format);
-/* Returns the buffer size (int frames) that should be used for pcm_write. */
unsigned int pcm_get_buffer_size(struct pcm *pcm);
unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames);
unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes);
-/* Returns available frames in pcm buffer and corresponding time stamp.
- * The clock is CLOCK_MONOTONIC if flag PCM_MONOTONIC was specified in 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.
- */
int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
struct timespec *tstamp);
-/* Returns the subdevice on which the pcm has been opened */
unsigned int pcm_get_subdevice(struct pcm *pcm);
-/* Write data to the fifo.
- * Will start playback on the first write or on a write that
- * occurs after a fifo underrun.
- */
int pcm_write(struct pcm *pcm, const void *data, unsigned int count);
int pcm_read(struct pcm *pcm, void *data, unsigned int count);
-/*
- * mmap() support.
- */
int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);
int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count);
int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
unsigned int *frames);
int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames);
-/* Prepare the PCM substream to be triggerable */
int pcm_prepare(struct pcm *pcm);
-/* Start and stop a PCM channel that doesn't transfer data */
int pcm_start(struct pcm *pcm);
int pcm_stop(struct pcm *pcm);
-/* Interrupt driven API */
int pcm_wait(struct pcm *pcm, int timeout);
-/* Get the pcm delay */
long pcm_get_delay(struct pcm *pcm);
#if defined(__cplusplus)
diff --git a/src/pcm.c b/src/pcm.c
index 24ab333..a45ce75 100644
--- a/src/pcm.c
+++ b/src/pcm.c
@@ -156,15 +156,26 @@ static void param_init(struct snd_pcm_hw_params *p)
#define PCM_ERROR_MAX 128
+/** A PCM handle.
+ * @ingroup tinyalsa-pcm
+ */
struct pcm {
+ /** The PCM's file descriptor */
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;
+ /** Size of the buffer */
unsigned int buffer_size;
unsigned int boundary;
+ /** Description of the last error that occured */
char error[PCM_ERROR_MAX];
+ /** Configuration that was passed to @ref pcm_open */
struct pcm_config config;
struct snd_pcm_mmap_status *mmap_status;
struct snd_pcm_mmap_control *mmap_control;
@@ -175,21 +186,40 @@ struct pcm {
unsigned int subdevice;
};
+/** Gets the buffer size of the PCM.
+ * @param pcm A PCM handle.
+ * @return The buffer size of the PCM.
+ * @ingroup tinyalsa-pcm
+ */
unsigned int pcm_get_buffer_size(struct pcm *pcm)
{
return pcm->buffer_size;
}
+/** Gets the file descriptor of the PCM.
+ * Useful for extending functionality of the PCM when needed.
+ * @return The file descriptor of the PCM.
+ * @ingroup tinyalsa-pcm
+ */
int pcm_get_file_descriptor(struct pcm *pcm)
{
return pcm->fd;
}
+/** Gets the error message for the last error that occured.
+ * If no error occured and this function is called, the results are undefined.
+ * @param pcm A PCM handle.
+ * @return The error message of the last error that occured.
+ * @ingroup tinyalsa-pcm
+ */
const char* pcm_get_error(struct pcm *pcm)
{
return pcm->error;
}
+/** Gets the subdevice on which the pcm has been opened.
+ * @param pcm A PCM handle.
+ * @return The subdevice on which the pcm has been opened */
unsigned int pcm_get_subdevice(struct pcm *pcm)
{
return pcm->subdevice;
@@ -241,6 +271,11 @@ static unsigned int pcm_format_to_alsa(enum pcm_format format)
};
}
+/** Determines the number of bits occupied by a @ref pcm_format.
+ * @param format A PCM format.
+ * @return The number of bits associated with @p format
+ * @ingroup tinyalsa-pcm
+ */
unsigned int pcm_format_to_bits(enum pcm_format format)
{
switch (format) {
@@ -261,12 +296,24 @@ unsigned int pcm_format_to_bits(enum pcm_format format)
};
}
+/** Determines how many frames of a PCM can fit into a number of bytes.
+ * @param pcm A PCM handle.
+ * @param bytes The number of bytes.
+ * @return The number of frames that may fit into @p bytes
+ * @ingroup tinyalsa-pcm
+ */
unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
{
return bytes / (pcm->config.channels *
(pcm_format_to_bits(pcm->config.format) >> 3));
}
+/** Determines how many bytes are occupied by a number of frames of a PCM.
+ * @param pcm A PCM handle.
+ * @param frames The number of frames of a PCM.
+ * @return The bytes occupied by @p frames.
+ * @ingroup tinyalsa-pcm
+ */
unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
{
return frames * pcm->config.channels *
@@ -381,6 +428,17 @@ 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)
{
@@ -417,6 +475,16 @@ int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
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 count The number of bytes occupied by the sample array.
+ * @return On success, this function returns zero; otherwise, a negative number.
+ * @ingroup tinyalsa-pcm
+ */
int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
{
struct snd_xferi x;
@@ -456,6 +524,16 @@ int pcm_write(struct pcm *pcm, const void *data, unsigned int 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.
+ * @ingroup tinyalsa-pcm
+ */
int pcm_read(struct pcm *pcm, void *data, unsigned int count)
{
struct snd_xferi x;
@@ -492,6 +570,18 @@ static struct pcm bad_pcm = {
.fd = -1,
};
+/** Gets the hardware parameters of a PCM, without created a PCM handle.
+ * @param card The card of the PCM.
+ * The default card is zero.
+ * @param device The device of the PCM.
+ * The default device is zero.
+ * @param flags Specifies whether the PCM is an input or output.
+ * May be one of the following:
+ * - @ref PCM_IN
+ * - @ref PCM_OUT
+ * @return On success, the hardware parameters of the PCM; on failure, NULL.
+ * @ingroup tinyalsa-pcm
+ */
struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
unsigned int flags)
{
@@ -530,6 +620,11 @@ err_open:
return NULL;
}
+/** Frees the hardware parameters returned by @ref pcm_params_open.
+ * @param pcm_params Hardware parameters of a PCM.
+ * May be NULL.
+ * @ingroup tinyalsa-pcm
+ */
void pcm_params_free(struct pcm_params *pcm_params)
{
struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
@@ -589,6 +684,13 @@ static int pcm_param_to_alsa(enum pcm_param param)
}
}
+/** Gets a mask from a PCM's hardware parameters.
+ * @param pcm_params A PCM's hardware parameters.
+ * @param param The parameter to get.
+ * @return If @p pcm_params is NULL or @p param is not a mask, NULL is returned.
+ * Otherwise, the mask associated with @p param is returned.
+ * @ingroup tinyalsa-pcm
+ */
struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params,
enum pcm_param param)
{
@@ -638,6 +740,12 @@ unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
return param_get_max(params, p);
}
+/** Closes a PCM returned by @ref pcm_open.
+ * @param pcm A PCM returned by @ref pcm_open.
+ * May not be NULL.
+ * @return Always returns zero.
+ * @ingroup tinyalsa-pcm
+ */
int pcm_close(struct pcm *pcm)
{
if (pcm == &bad_pcm)
@@ -660,6 +768,23 @@ int pcm_close(struct pcm *pcm)
return 0;
}
+/** Opens a PCM.
+ * @param card The card that the pcm belongs to.
+ * The default card is zero.
+ * @param device The device that the pcm belongs to.
+ * The default device is zero.
+ * @param flags Specify characteristics and functionality about the pcm.
+ * May be a bitwise AND of the following:
+ * - @ref PCM_IN
+ * - @ref PCM_OUT
+ * - @ref PCM_MMAP
+ * - @ref PCM_NOIRQ
+ * - @ref PCM_MONOTONIC
+ * @param config The hardware and software parameters to open the PCM with.
+ * @returns On success, returns an initialized pcm, ready for reading or writing.
+ * On error, returns NULL.
+ * @ingroup tinyalsa-pcm
+ */
struct pcm *pcm_open(unsigned int card, unsigned int device,
unsigned int flags, struct pcm_config *config)
{
@@ -814,11 +939,22 @@ fail_close:
return pcm;
}
+/** Checks if a PCM file has been opened without error.
+ * @param pcm A PCM handle.
+ * @return If a PCM's file descriptor is not valid, it returns zero.
+ * Otherwise, the function returns one.
+ * @ingroup tinyalsa-pcm
+ */
int pcm_is_ready(struct pcm *pcm)
{
return pcm->fd >= 0;
}
+/** Prepares a PCM, if it has not been prepared already.
+ * @param pcm A PCM handle.
+ * @return On success, zero; on failure, a negative number.
+ * @ingroup tinyalsa-pcm
+ */
int pcm_prepare(struct pcm *pcm)
{
if (pcm->prepared)
@@ -831,6 +967,13 @@ int pcm_prepare(struct pcm *pcm)
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 tinyalsa-pcm
+ */
int pcm_start(struct pcm *pcm)
{
int prepare_error = pcm_prepare(pcm);
@@ -847,6 +990,11 @@ int pcm_start(struct pcm *pcm)
return 0;
}
+/** Stops a PCM.
+ * @param pcm A PCM handle.
+ * @return On success, zero; on failure, a negative number.
+ * @ingroup tinyalsa-pcm
+ */
int pcm_stop(struct pcm *pcm)
{
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
@@ -1095,3 +1243,4 @@ long pcm_get_delay(struct pcm *pcm)
return pcm->pcm_delay;
}
+