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:
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"
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
#define RT_DEVICE_FLAG_RDONLY 0x001
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:
#define RT_DEVICE_OFLAG_WRONLY
Definition: rtdef.h:1307
Example for opening a recording device:
#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:
#define _AUDIO_CTL(a) (0x10 + a)
#define AUDIO_CTL_GETCAPS _AUDIO_CTL(1)
#define AUDIO_CTL_CONFIGURE _AUDIO_CTL(2)
Device capability structure:
{
union
{
};
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
Setting Playback Parameters
Configure playback sample rate, channels, and bit depth:
caps.udata.config.samplerate = 44100;
caps.udata.config.channels = 2;
caps.udata.config.samplebits = 16;
#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):
caps.udata.value = volume;
#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:
caps.udata.config.samplerate = 44100;
caps.udata.config.channels = 2;
caps.udata.config.samplebits = 16;
#define AUDIO_TYPE_INPUT
Definition: dev_audio.h:61
Setting Recording Volume
Adjust microphone gain (0-100):
caps.udata.value = volume;
#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:
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:
- First, find the Audio device and obtain the device handle.
- Open the Audio device in write-only mode.
- Set audio parameters (sampling rate, channels, etc.).
- Decode the audio file data.
- Write the audio file data.
- 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"
struct RIFF_HEADER_DEF
{
char riff_id[4];
uint32_t riff_size;
char riff_format[4];
};
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];
uint32_t fmt_size;
struct WAVE_FORMAT_DEF wav_format;
};
struct DATA_BLOCK_DEF
{
char data_id[4];
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;
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;
}
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);
snd_dev = rt_device_find(SOUND_DEVICE_NAME);
while (1)
{
int length;
length =
read(fd, buffer, BUFSZ);
if (length <= 0)
break;
rt_device_write(snd_dev, 0, buffer, length);
}
rt_device_close(snd_dev);
__exit:
if (fd >= 0)
if (buffer)
if (info)
return 0;
}
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
Recording
The main steps to record an audio file are as follows:
- First, find the Audio device and obtain the device handle.
- Open the Audio device in read-only mode.
- Set audio parameters (sampling rate, channels, etc.).
- Read data from the Audio device.
- Process the recorded data.
- 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"
struct wav_header
{
char riff_id[4];
int riff_datasize;
char riff_type[4];
char fmt_id[4];
int fmt_datasize;
short fmt_compression_code;
short fmt_channels;
int fmt_sample_rate;
int fmt_avg_bytes_per_sec;
short fmt_block_align;
short fmt_bit_per_sample;
char data_id[4];
int data_datasize;
};
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;
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));
if (buffer == RT_NULL)
goto __exit;
mic_dev = rt_device_find(SOUND_DEVICE_NAME);
if (mic_dev == RT_NULL)
goto __exit;
while (1)
{
length = rt_device_read(mic_dev, 0, buffer, RECORD_CHUNK_SZ);
if (length)
{
write(fd, buffer, length);
total_length += length;
}
if ((total_length / RECORD_CHUNK_SZ) > (RECORD_TIME_MS / 20))
break;
}
wavheader_init(&header, RECORD_SAMPLERATE, RECORD_CHANNEL, total_length);
write(fd, &header,
sizeof(
struct wav_header));
rt_device_close(mic_dev);
__exit:
if (fd >= 0)
if (buffer)
return 0;
}
#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