RT-Thread RTOS
An open source embedded real-time operating system
Audio Device

Audio Introduction

The Audio device is a crucial component in embedded systems, responsible for audio data sampling and output. An Audio device typically consists of a data bus interface, control bus interface, audio codec (Codec), speaker, and microphone, as shown below:

API List

For more details, see Audio

Audio Device Features

The RT-Thread Audio device driver framework serves as the underlying layer of the Audio framework. It handles raw audio data acquisition/output, audio stream control, device management, volume adjustment, and hardware/Codec abstraction.

  • Interface: Standard device interface (open/close/read/control).
  • Synchronous access mode.
  • Supports playback and recording.
  • Audio parameter management.
  • Volume control.

Accessing Audio Devices

Finding an Audio Device

Applications obtain a device handle using the Audio device name. The device lookup function is as follows:

rt_device_t rt_device_find(const char* name);
Definition: rtdef.h:1373
Parameter Description
name Audio device name
Return ——
Device handle Returns the handle if found
RT_NULL Device not found

Example usage:

#define SOUND_DEVICE_NAME "sound0" /* Audio device name */
static rt_device_t snd_dev; /* Audio device handle */
/* Find Audio device by name and obtain handle */
snd_dev = rt_device_find(SOUND_DEVICE_NAME);

Opening an Audio Device

Applications can open/close devices using the device handle. To open a device:

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
Parameter Description
dev Device handle
oflags Device mode flags
Return ——
RT_EOK Device opened successfully
-RT_EBUSY Device cannot be reopened if registered with RT_DEVICE_FLAG_STANDALONE
-RT_EINVAL Unsupported open flags
Other errors Device open failure

Supported oflags values:

#define RT_DEVICE_OFLAG_WRONLY 0x002 /* Write-only mode for playback devices */
#define RT_DEVICE_FLAG_RDONLY 0x001 /* Read-only mode for recording devices */

Audio devices are categorized into playback (output) and recording (input). Playback devices use the write-only flag, while recording devices use read-only.

Example for opening a playback device:

rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY)
#define RT_DEVICE_OFLAG_WRONLY
Definition: rtdef.h:1307

Example for opening a recording device:

rt_device_open(mic_dev, RT_DEVICE_FLAG_RDONLY)
#define RT_DEVICE_FLAG_RDONLY
Definition: rtdef.h:1289

Controlling an Audio Device

Applications configure Audio devices using control commands:

rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
Parameter Description
dev Device handle
cmd Control command (see below)
arg Control parameter (see below)
Return ——
RT_EOK Operation succeeded
-RT_ENOSYS Failed (null device handle)
Other errors Operation failed

Supported control commands:

/* AUDIO commands */
#define _AUDIO_CTL(a) (0x10 + a)
#define AUDIO_CTL_GETCAPS _AUDIO_CTL(1) /* Get device capabilities */
#define AUDIO_CTL_CONFIGURE _AUDIO_CTL(2) /* Configure device */

Device capability structure:

{
int main_type; /* Main command type */
int sub_type; /* Sub command type */
union
{
rt_uint32_t mask;
int value; /* Parameter value */
struct rt_audio_configure config; /* Audio configuration */
} udata;
};
Audio capabilities.
Definition: dev_audio.h:217
struct rt_audio_configure config
Definition: dev_audio.h:225
union rt_audio_caps::@2 udata
int value
Definition: dev_audio.h:224
int main_type
Definition: dev_audio.h:218
int sub_type
Definition: dev_audio.h:219
rt_uint32_t mask
Definition: dev_audio.h:223
Audio configuration.
Definition: dev_audio.h:207

Setting Playback Parameters

Configure playback sample rate, channels, and bit depth:

struct rt_audio_caps caps;
caps.main_type = AUDIO_TYPE_OUTPUT; /* Playback device */
caps.sub_type = AUDIO_DSP_PARAM; /* Set all audio parameters */
caps.udata.config.samplerate = 44100; /* Sample rate */
caps.udata.config.channels = 2; /* Channels */
caps.udata.config.samplebits = 16; /* Bit depth */
rt_device_control(device, AUDIO_CTL_CONFIGURE, &caps);
#define AUDIO_CTL_CONFIGURE
Definition: dev_audio.h:46
#define AUDIO_DSP_PARAM
Definition: dev_audio.h:113
#define AUDIO_TYPE_OUTPUT
Definition: dev_audio.h:62

Setting Playback Volume

Adjust master playback volume (0-100):

struct rt_audio_caps caps;
caps.main_type = AUDIO_TYPE_MIXER; /* Volume control type */
caps.sub_type = AUDIO_MIXER_VOLUME; /* Set master volume */
caps.udata.value = volume; /* Range: 0-100 */
rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);
#define AUDIO_MIXER_VOLUME
Definition: dev_audio.h:129
#define AUDIO_TYPE_MIXER
Definition: dev_audio.h:63

Setting Recording Parameters

Configure recording sample rate, channels, and bit depth:

struct rt_audio_caps caps;
caps.main_type = AUDIO_TYPE_INPUT; /* Recording device */
caps.sub_type = AUDIO_DSP_PARAM; /* Set all audio parameters */
caps.udata.config.samplerate = 44100; /* Sample rate */
caps.udata.config.channels = 2; /* Channels */
caps.udata.config.samplebits = 16; /* Bit depth */
rt_device_control(device, AUDIO_CTL_CONFIGURE, &caps);
#define AUDIO_TYPE_INPUT
Definition: dev_audio.h:61

Setting Recording Volume

Adjust microphone gain (0-100):

struct rt_audio_caps caps;
caps.main_type = AUDIO_TYPE_MIXER; /* Volume control type */
caps.sub_type = AUDIO_MIXER_MIC; /* Set microphone gain */
caps.udata.value = volume; /* Range: 0-100 */
rt_device_control(player->device, AUDIO_CTL_CONFIGURE, &caps);
#define AUDIO_MIXER_MIC
Definition: dev_audio.h:136

Writing Audio Data

Write data to a playback device:

rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
Parameter Description
dev Device handle
pos Unused (reserved for offset)
buffer Data buffer to write
size Data size to write
Return ——
Bytes written Actual bytes written (synchronous operation)

This synchronous function writes data to the device's internal buffer. The call blocks when the buffer is full.

Reading Audio Data

Read data from a recording device:

rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
Parameter Description
dev Device handle
pos Unused (reserved for offset)
buffer Buffer to store read data
size Data size to read
Return ——
Bytes read Actual bytes read (synchronous operation)
0 Check thread's errno for error status

This synchronous function reads data from the device's internal pipe. The call blocks if insufficient data is available.

Closing an Audio Device

Close the device after operations:

rt_err_t rt_device_close(rt_device_t dev);
Parameter Description
dev Device handle
Return ——
RT_EOK Device closed successfully
-RT_ERROR Device already closed
Other errors Close failure

Audio Device Usage Example

Audio devices are used for playback and recording, often accompanied by audio file encoding and decoding. Below are examples of playing and recording WAV files. The complete code can be obtained from the RT-Thread WAV Player Package.

Playback

The main steps to play an audio file are as follows:

  1. First, find the Audio device and obtain the device handle.
  2. Open the Audio device in write-only mode.
  3. Set audio parameters (sampling rate, channels, etc.).
  4. Decode the audio file data.
  5. Write the audio file data.
  6. After playback is complete, close the device.
#include <rtthread.h>
#include <rtdevice.h>
#include <dfs_posix.h>
#define BUFSZ 1024
#define SOUND_DEVICE_NAME "sound0" /* Audio device name */
static rt_device_t snd_dev; /* Audio device handle */
struct RIFF_HEADER_DEF
{
char riff_id[4]; // 'R','I','F','F'
uint32_t riff_size;
char riff_format[4]; // 'W','A','V','E'
};
struct WAVE_FORMAT_DEF
{
uint16_t FormatTag;
uint16_t Channels;
uint32_t SamplesPerSec;
uint32_t AvgBytesPerSec;
uint16_t BlockAlign;
uint16_t BitsPerSample;
};
struct FMT_BLOCK_DEF
{
char fmt_id[4]; // 'f','m','t',' '
uint32_t fmt_size;
struct WAVE_FORMAT_DEF wav_format;
};
struct DATA_BLOCK_DEF
{
char data_id[4]; // 'R','I','F','F'
uint32_t data_size;
};
struct wav_info
{
struct RIFF_HEADER_DEF header;
struct FMT_BLOCK_DEF fmt_block;
struct DATA_BLOCK_DEF data_block;
};
int wavplay_sample(int argc, char **argv)
{
int fd = -1;
uint8_t *buffer = NULL;
struct wav_info *info = NULL;
struct rt_audio_caps caps = {0};
if (argc != 2)
{
rt_kprintf("Usage:\n");
rt_kprintf("wavplay_sample song.wav\n");
return 0;
}
fd = open(argv[1], O_RDONLY);
if (fd < 0)
{
rt_kprintf("open file failed!\n");
goto __exit;
}
buffer = rt_malloc(BUFSZ);
if (buffer == RT_NULL)
goto __exit;
info = (struct wav_info *) rt_malloc(sizeof * info);
if (info == RT_NULL)
goto __exit;
if (read(fd, &(info->header), sizeof(struct RIFF_HEADER_DEF)) <= 0)
goto __exit;
if (read(fd, &(info->fmt_block), sizeof(struct FMT_BLOCK_DEF)) <= 0)
goto __exit;
if (read(fd, &(info->data_block), sizeof(struct DATA_BLOCK_DEF)) <= 0)
goto __exit;
rt_kprintf("wav information:\n");
rt_kprintf("samplerate %d\n", info->fmt_block.wav_format.SamplesPerSec);
rt_kprintf("channel %d\n", info->fmt_block.wav_format.Channels);
/* Find the Audio device by name and obtain the device handle */
snd_dev = rt_device_find(SOUND_DEVICE_NAME);
/* Open the Audio playback device in write-only mode */
rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY);
/* Set audio parameters such as sampling rate, channels, and bit depth */
caps.main_type = AUDIO_TYPE_OUTPUT; /* Output type (playback device) */
caps.sub_type = AUDIO_DSP_PARAM; /* Set all audio parameters */
caps.udata.config.samplerate = info->fmt_block.wav_format.SamplesPerSec; /* Sampling rate */
caps.udata.config.channels = info->fmt_block.wav_format.Channels; /* Channels */
caps.udata.config.samplebits = 16; /* Bit depth */
rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);
while (1)
{
int length;
/* Read audio data from the file system */
length = read(fd, buffer, BUFSZ);
if (length <= 0)
break;
/* Write audio data to the Audio device */
rt_device_write(snd_dev, 0, buffer, length);
}
/* Close the Audio device */
rt_device_close(snd_dev);
__exit:
if (fd >= 0)
close(fd);
if (buffer)
rt_free(buffer);
if (info)
rt_free(info);
return 0;
}
MSH_CMD_EXPORT(wavplay_sample, play wav file);
ssize_t read(int fd, void *buf, size_t len)
Definition: dfs_posix.c:325
int open(const char *file, int flags,...)
Definition: dfs_posix.c:48
int close(int fd)
Definition: dfs_posix.c:284
rt_weak void * rt_malloc(rt_size_t size)
Allocate a block of memory with a minimum of 'size' bytes.
Definition: kservice.c:807
rt_weak void rt_free(void *ptr)
This function will release the previously allocated memory block by rt_malloc. The released memory bl...
Definition: kservice.c:886
#define MSH_CMD_EXPORT(...)
Exports a command to module shell.
Definition: finsh.h:151
rt_uint32_t samplerate
Definition: dev_audio.h:208
rt_uint16_t channels
Definition: dev_audio.h:209
rt_uint16_t samplebits
Definition: dev_audio.h:210

Recording

The main steps to record an audio file are as follows:

  1. First, find the Audio device and obtain the device handle.
  2. Open the Audio device in read-only mode.
  3. Set audio parameters (sampling rate, channels, etc.).
  4. Read data from the Audio device.
  5. Process the recorded data.
  6. After recording is complete, close the device.
#include <rtthread.h>
#include <rtdevice.h>
#include <dfs_posix.h>
#define RECORD_TIME_MS 5000
#define RECORD_SAMPLERATE 16000
#define RECORD_CHANNEL 2
#define RECORD_CHUNK_SZ ((RECORD_SAMPLERATE * RECORD_CHANNEL * 2) * 20 / 1000)
#define SOUND_DEVICE_NAME "mic0" /* Audio device name */
static rt_device_t mic_dev; /* Audio device handle */
struct wav_header
{
char riff_id[4]; /* "RIFF" */
int riff_datasize; /* RIFF chunk data size, excluding riff_id[4] and riff_datasize, total - 8 */
char riff_type[4]; /* "WAVE" */
char fmt_id[4]; /* "fmt " */
int fmt_datasize; /* fmt chunk data size, 16 for PCM */
short fmt_compression_code; /* 1 for PCM */
short fmt_channels; /* 1(mono) or 2(stereo) */
int fmt_sample_rate; /* samples per second */
int fmt_avg_bytes_per_sec; /* sample_rate * channels * bit_per_sample / 8 */
short fmt_block_align; /* number of bytes per sample, bit_per_sample * channels / 8 */
short fmt_bit_per_sample; /* bits of each sample(8,16,32). */
char data_id[4]; /* "data" */
int data_datasize; /* data chunk size, pcm_size - 44 */
};
static void wavheader_init(struct wav_header *header, int sample_rate, int channels, int datasize)
{
memcpy(header->riff_id, "RIFF", 4);
header->riff_datasize = datasize + 44 - 8;
memcpy(header->riff_type, "WAVE", 4);
memcpy(header->fmt_id, "fmt ", 4);
header->fmt_datasize = 16;
header->fmt_compression_code = 1;
header->fmt_channels = channels;
header->fmt_sample_rate = sample_rate;
header->fmt_bit_per_sample = 16;
header->fmt_avg_bytes_per_sec = header->fmt_sample_rate * header->fmt_channels * header->fmt_bit_per_sample / 8;
header->fmt_block_align = header->fmt_bit_per_sample * header->fmt_channels / 8;
memcpy(header->data_id, "data", 4);
header->data_datasize = datasize;
}
int wavrecord_sample(int argc, char **argv)
{
int fd = -1;
uint8_t *buffer = NULL;
struct wav_header header;
struct rt_audio_caps caps = {0};
int length, total_length = 0;
if (argc != 2)
{
rt_kprintf("Usage:\n");
rt_kprintf("wavrecord_sample file.wav\n");
return -1;
}
fd = open(argv[1], O_WRONLY | O_CREAT);
if (fd < 0)
{
rt_kprintf("open file for recording failed!\n");
return -1;
}
write(fd, &header, sizeof(struct wav_header));
buffer = rt_malloc(RECORD_CHUNK_SZ);
if (buffer == RT_NULL)
goto __exit;
/* Find the Audio device by name and obtain the device handle */
mic_dev = rt_device_find(SOUND_DEVICE_NAME);
if (mic_dev == RT_NULL)
goto __exit;
/* Open the Audio recording device in read-only mode */
rt_device_open(mic_dev, RT_DEVICE_OFLAG_RDONLY);
/* Set audio parameters such as sampling rate, channels, and bit depth */
caps.main_type = AUDIO_TYPE_INPUT; /* Input type (recording device) */
caps.sub_type = AUDIO_DSP_PARAM; /* Set all audio parameters */
caps.udata.config.samplerate = RECORD_SAMPLERATE; /* Sampling rate */
caps.udata.config.channels = RECORD_CHANNEL; /* Channels */
caps.udata.config.samplebits = 16; /* Bit depth */
rt_device_control(mic_dev, AUDIO_CTL_CONFIGURE, &caps);
while (1)
{
/* Read 20ms of audio data from the Audio device */
length = rt_device_read(mic_dev, 0, buffer, RECORD_CHUNK_SZ);
if (length)
{
/* Write the audio data to the file system */
write(fd, buffer, length);
total_length += length;
}
if ((total_length / RECORD_CHUNK_SZ) > (RECORD_TIME_MS / 20))
break;
}
/* Rewrite the WAV file header */
wavheader_init(&header, RECORD_SAMPLERATE, RECORD_CHANNEL, total_length);
lseek(fd, 0, SEEK_SET);
write(fd, &header, sizeof(struct wav_header));
close(fd);
/* Close the Audio device */
rt_device_close(mic_dev);
__exit:
if (fd >= 0)
close(fd);
if (buffer)
rt_free(buffer);
return 0;
}
MSH_CMD_EXPORT(wavrecord_sample, record voice to a wav file);
#define RT_DEVICE_OFLAG_RDONLY
Definition: rtdef.h:1306
ssize_t write(int fd, const void *buf, size_t len)
Definition: dfs_posix.c:370
off_t lseek(int fd, off_t offset, int whence)
Definition: dfs_posix.c:420