1@page page_device_audio Audio Device
2
3# Audio Introduction
4
5The 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:
6
7## API List
8
9For more details, see @ref group_drivers_audio
10
11## Audio Device Features
12
13The 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.
14
15- **Interface**: Standard device interface (`open/close/read/control`).
16- **Synchronous access mode**.
17- **Supports playback and recording**.
18- **Audio parameter management**.
19- **Volume control**.
20
21# Accessing Audio Devices
22
23## Finding an Audio Device
24
25Applications obtain a device handle using the Audio device name. The device lookup function is as follows:
26
27```c
28rt_device_t rt_device_find(const char* name);
29```
30
31| **Parameter** | **Description**                       |
32|---------------|---------------------------------------|
33| name          | Audio device name                     |
34| **Return**    | ——                                   |
35| Device handle | Returns the handle if found           |
36| RT_NULL       | Device not found                      |
37
38Example usage:
39```c
40#define SOUND_DEVICE_NAME    "sound0"    /* Audio device name */
41
42static rt_device_t snd_dev;              /* Audio device handle */
43
44/* Find Audio device by name and obtain handle */
45snd_dev = rt_device_find(SOUND_DEVICE_NAME);
46```
47
48## Opening an Audio Device
49
50Applications can open/close devices using the device handle. To open a device:
51
52```c
53rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
54```
55
56| **Parameter** | **Description**                                                                 |
57|---------------|---------------------------------------------------------------------------------|
58| dev           | Device handle                                                                   |
59| oflags        | Device mode flags                                                               |
60| **Return**    | ——                                                                             |
61| RT_EOK        | Device opened successfully                                                     |
62| -RT_EBUSY     | Device cannot be reopened if registered with `RT_DEVICE_FLAG_STANDALONE`        |
63| -RT_EINVAL    | Unsupported open flags                                                         |
64| Other errors  | Device open failure                                                            |
65
66Supported `oflags` values:
67```c
68#define RT_DEVICE_OFLAG_WRONLY      0x002     /* Write-only mode for playback devices */
69#define RT_DEVICE_FLAG_RDONLY       0x001     /* Read-only mode for recording devices */
70```
71
72Audio devices are categorized into playback (output) and recording (input). Playback devices use the write-only flag, while recording devices use read-only.
73
74Example for opening a playback device:
75```c
76rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY)
77```
78
79Example for opening a recording device:
80```c
81rt_device_open(mic_dev, RT_DEVICE_FLAG_RDONLY)
82```
83
84## Controlling an Audio Device
85
86Applications configure Audio devices using control commands:
87
88```c
89rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
90```
91
92| **Parameter** | **Description**                               |
93|---------------|-----------------------------------------------|
94| dev           | Device handle                                 |
95| cmd           | Control command (see below)                   |
96| arg           | Control parameter (see below)                 |
97| **Return**    | ——                                           |
98| RT_EOK        | Operation succeeded                           |
99| -RT_ENOSYS    | Failed (null device handle)                   |
100| Other errors  | Operation failed                              |
101
102Supported control commands:
103```c
104/* AUDIO commands */
105#define _AUDIO_CTL(a) (0x10 + a)
106
107#define AUDIO_CTL_GETCAPS                   _AUDIO_CTL(1) /* Get device capabilities */
108#define AUDIO_CTL_CONFIGURE                 _AUDIO_CTL(2) /* Configure device */
109```
110
111Device capability structure:
112```c
113struct rt_audio_caps
114{
115    int main_type;                            /* Main command type */
116    int sub_type;                             /* Sub command type */
117
118    union
119    {
120        rt_uint32_t mask;
121        int     value;                       /* Parameter value */
122        struct rt_audio_configure config;    /* Audio configuration */
123    } udata;
124};
125```
126
127### Setting Playback Parameters
128
129Configure playback sample rate, channels, and bit depth:
130```c
131struct rt_audio_caps caps;
132
133caps.main_type               = AUDIO_TYPE_OUTPUT; /* Playback device */
134caps.sub_type                = AUDIO_DSP_PARAM;   /* Set all audio parameters */
135caps.udata.config.samplerate = 44100;             /* Sample rate */
136caps.udata.config.channels   = 2;                 /* Channels */
137caps.udata.config.samplebits = 16;                /* Bit depth */
138rt_device_control(device, AUDIO_CTL_CONFIGURE, &caps);
139```
140
141### Setting Playback Volume
142
143Adjust master playback volume (0-100):
144```c
145struct rt_audio_caps caps;
146
147caps.main_type   = AUDIO_TYPE_MIXER;       /* Volume control type */
148caps.sub_type    = AUDIO_MIXER_VOLUME;     /* Set master volume */
149caps.udata.value = volume;                 /* Range: 0-100 */
150rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);
151```
152
153### Setting Recording Parameters
154
155Configure recording sample rate, channels, and bit depth:
156```c
157struct rt_audio_caps caps;
158
159caps.main_type               = AUDIO_TYPE_INPUT;  /* Recording device */
160caps.sub_type                = AUDIO_DSP_PARAM;   /* Set all audio parameters */
161caps.udata.config.samplerate = 44100;             /* Sample rate */
162caps.udata.config.channels   = 2;                 /* Channels */
163caps.udata.config.samplebits = 16;                /* Bit depth */
164rt_device_control(device, AUDIO_CTL_CONFIGURE, &caps);
165```
166
167### Setting Recording Volume
168
169Adjust microphone gain (0-100):
170```c
171struct rt_audio_caps caps;
172
173caps.main_type = AUDIO_TYPE_MIXER;       /* Volume control type */
174caps.sub_type  = AUDIO_MIXER_MIC;        /* Set microphone gain */
175caps.udata.value = volume;               /* Range: 0-100 */
176rt_device_control(player->device, AUDIO_CTL_CONFIGURE, &caps);
177```
178
179## Writing Audio Data
180
181Write data to a playback device:
182```c
183rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
184```
185
186| **Parameter** | **Description**                               |
187|---------------|-----------------------------------------------|
188| dev           | Device handle                                 |
189| pos           | Unused (reserved for offset)                  |
190| buffer        | Data buffer to write                          |
191| size          | Data size to write                            |
192| **Return**    | ——                                           |
193| Bytes written | Actual bytes written (synchronous operation)  |
194
195This synchronous function writes data to the device's internal buffer. The call blocks when the buffer is full.
196
197## Reading Audio Data
198
199Read data from a recording device:
200```c
201rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
202```
203
204| **Parameter** | **Description**                               |
205|---------------|-----------------------------------------------|
206| dev           | Device handle                                 |
207| pos           | Unused (reserved for offset)                  |
208| buffer        | Buffer to store read data                     |
209| size          | Data size to read                             |
210| **Return**    | ——                                           |
211| Bytes read    | Actual bytes read (synchronous operation)     |
212| 0             | Check thread's errno for error status         |
213
214This synchronous function reads data from the device's internal pipe. The call blocks if insufficient data is available.
215
216## Closing an Audio Device
217
218Close the device after operations:
219```c
220rt_err_t rt_device_close(rt_device_t dev);
221```
222
223| **Parameter** | **Description**                       |
224|---------------|---------------------------------------|
225| dev           | Device handle                         |
226| **Return**    | ——                                   |
227| RT_EOK        | Device closed successfully           |
228| -RT_ERROR     | Device already closed                 |
229| Other errors  | Close failure                        |
230
231# Audio Device Usage Example
232
233Audio 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](https://github.com/RT-Thread-packages/wavplayer).
234
235## Playback
236
237The main steps to play an audio file are as follows:
238
2391. First, find the Audio device and obtain the device handle.
2402. Open the Audio device in write-only mode.
2413. Set audio parameters (sampling rate, channels, etc.).
2424. Decode the audio file data.
2435. Write the audio file data.
2446. After playback is complete, close the device.
245
246```c
247#include <rtthread.h>
248#include <rtdevice.h>
249#include <dfs_posix.h>
250
251#define BUFSZ   1024
252#define SOUND_DEVICE_NAME    "sound0"    /* Audio device name */
253static rt_device_t snd_dev;              /* Audio device handle */
254
255struct RIFF_HEADER_DEF
256{
257    char riff_id[4];     // 'R','I','F','F'
258    uint32_t riff_size;
259    char riff_format[4]; // 'W','A','V','E'
260};
261
262struct WAVE_FORMAT_DEF
263{
264    uint16_t FormatTag;
265    uint16_t Channels;
266    uint32_t SamplesPerSec;
267    uint32_t AvgBytesPerSec;
268    uint16_t BlockAlign;
269    uint16_t BitsPerSample;
270};
271
272struct FMT_BLOCK_DEF
273{
274    char fmt_id[4];    // 'f','m','t',' '
275    uint32_t fmt_size;
276    struct WAVE_FORMAT_DEF wav_format;
277};
278
279struct DATA_BLOCK_DEF
280{
281    char data_id[4];     // 'R','I','F','F'
282    uint32_t data_size;
283};
284
285struct wav_info
286{
287    struct RIFF_HEADER_DEF header;
288    struct FMT_BLOCK_DEF   fmt_block;
289    struct DATA_BLOCK_DEF  data_block;
290};
291
292int wavplay_sample(int argc, char **argv)
293{
294    int fd = -1;
295    uint8_t *buffer = NULL;
296    struct wav_info *info = NULL;
297    struct rt_audio_caps caps = {0};
298
299    if (argc != 2)
300    {
301        rt_kprintf("Usage:\n");
302        rt_kprintf("wavplay_sample song.wav\n");
303        return 0;
304    }
305
306    fd = open(argv[1], O_RDONLY);
307    if (fd < 0)
308    {
309        rt_kprintf("open file failed!\n");
310        goto __exit;
311    }
312
313    buffer = rt_malloc(BUFSZ);
314    if (buffer == RT_NULL)
315        goto __exit;
316
317    info = (struct wav_info *) rt_malloc(sizeof * info);
318    if (info == RT_NULL)
319        goto __exit;
320
321    if (read(fd, &(info->header), sizeof(struct RIFF_HEADER_DEF)) <= 0)
322        goto __exit;
323    if (read(fd, &(info->fmt_block),  sizeof(struct FMT_BLOCK_DEF)) <= 0)
324        goto __exit;
325    if (read(fd, &(info->data_block), sizeof(struct DATA_BLOCK_DEF)) <= 0)
326        goto __exit;
327
328    rt_kprintf("wav information:\n");
329    rt_kprintf("samplerate %d\n", info->fmt_block.wav_format.SamplesPerSec);
330    rt_kprintf("channel %d\n", info->fmt_block.wav_format.Channels);
331
332    /* Find the Audio device by name and obtain the device handle */
333    snd_dev = rt_device_find(SOUND_DEVICE_NAME);
334
335    /* Open the Audio playback device in write-only mode */
336    rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY);
337
338    /* Set audio parameters such as sampling rate, channels, and bit depth */
339    caps.main_type               = AUDIO_TYPE_OUTPUT;                           /* Output type (playback device) */
340    caps.sub_type                = AUDIO_DSP_PARAM;                             /* Set all audio parameters */
341    caps.udata.config.samplerate = info->fmt_block.wav_format.SamplesPerSec;    /* Sampling rate */
342    caps.udata.config.channels   = info->fmt_block.wav_format.Channels;         /* Channels */
343    caps.udata.config.samplebits = 16;                                          /* Bit depth */
344    rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);
345
346    while (1)
347    {
348        int length;
349
350        /* Read audio data from the file system */
351        length = read(fd, buffer, BUFSZ);
352
353        if (length <= 0)
354            break;
355
356        /* Write audio data to the Audio device */
357        rt_device_write(snd_dev, 0, buffer, length);
358    }
359
360    /* Close the Audio device */
361    rt_device_close(snd_dev);
362
363__exit:
364
365    if (fd >= 0)
366        close(fd);
367
368    if (buffer)
369        rt_free(buffer);
370
371    if (info)
372        rt_free(info);
373
374    return 0;
375}
376
377MSH_CMD_EXPORT(wavplay_sample,  play wav file);
378```
379
380## Recording
381
382The main steps to record an audio file are as follows:
383
3841. First, find the Audio device and obtain the device handle.
3852. Open the Audio device in read-only mode.
3863. Set audio parameters (sampling rate, channels, etc.).
3874. Read data from the Audio device.
3885. Process the recorded data.
3896. After recording is complete, close the device.
390
391```c
392#include <rtthread.h>
393#include <rtdevice.h>
394#include <dfs_posix.h>
395
396#define RECORD_TIME_MS      5000
397#define RECORD_SAMPLERATE   16000
398#define RECORD_CHANNEL      2
399#define RECORD_CHUNK_SZ     ((RECORD_SAMPLERATE * RECORD_CHANNEL * 2) * 20 / 1000)
400
401#define SOUND_DEVICE_NAME    "mic0"      /* Audio device name */
402static rt_device_t mic_dev;              /* Audio device handle */
403
404struct wav_header
405{
406    char  riff_id[4];              /* "RIFF" */
407    int   riff_datasize;           /* RIFF chunk data size, excluding riff_id[4] and riff_datasize, total - 8 */
408    char  riff_type[4];            /* "WAVE" */
409    char  fmt_id[4];               /* "fmt " */
410    int   fmt_datasize;            /* fmt chunk data size, 16 for PCM */
411    short fmt_compression_code;    /* 1 for PCM */
412    short fmt_channels;            /* 1(mono) or 2(stereo) */
413    int   fmt_sample_rate;         /* samples per second */
414    int   fmt_avg_bytes_per_sec;   /* sample_rate * channels * bit_per_sample / 8 */
415    short fmt_block_align;         /* number of bytes per sample, bit_per_sample * channels / 8 */
416    short fmt_bit_per_sample;      /* bits of each sample(8,16,32). */
417    char  data_id[4];              /* "data" */
418    int   data_datasize;           /* data chunk size, pcm_size - 44 */
419};
420
421static void wavheader_init(struct wav_header *header, int sample_rate, int channels, int datasize)
422{
423    memcpy(header->riff_id, "RIFF", 4);
424    header->riff_datasize = datasize + 44 - 8;
425    memcpy(header->riff_type, "WAVE", 4);
426    memcpy(header->fmt_id, "fmt ", 4);
427    header->fmt_datasize = 16;
428    header->fmt_compression_code = 1;
429    header->fmt_channels = channels;
430    header->fmt_sample_rate = sample_rate;
431    header->fmt_bit_per_sample = 16;
432    header->fmt_avg_bytes_per_sec = header->fmt_sample_rate * header->fmt_channels * header->fmt_bit_per_sample / 8;
433    header->fmt_block_align = header->fmt_bit_per_sample * header->fmt_channels / 8;
434    memcpy(header->data_id, "data", 4);
435    header->data_datasize = datasize;
436}
437
438int wavrecord_sample(int argc, char **argv)
439{
440    int fd = -1;
441    uint8_t *buffer = NULL;
442    struct wav_header header;
443    struct rt_audio_caps caps = {0};
444    int length, total_length = 0;
445
446    if (argc != 2)
447    {
448        rt_kprintf("Usage:\n");
449        rt_kprintf("wavrecord_sample file.wav\n");
450        return -1;
451    }
452
453    fd = open(argv[1], O_WRONLY | O_CREAT);
454    if (fd < 0)
455    {
456        rt_kprintf("open file for recording failed!\n");
457        return -1;
458    }
459    write(fd, &header, sizeof(struct wav_header));
460
461    buffer = rt_malloc(RECORD_CHUNK_SZ);
462    if (buffer == RT_NULL)
463        goto __exit;
464
465    /* Find the Audio device by name and obtain the device handle */
466    mic_dev = rt_device_find(SOUND_DEVICE_NAME);
467    if (mic_dev == RT_NULL)
468        goto __exit;
469
470    /* Open the Audio recording device in read-only mode */
471    rt_device_open(mic_dev, RT_DEVICE_OFLAG_RDONLY);
472
473    /* Set audio parameters such as sampling rate, channels, and bit depth */
474    caps.main_type               = AUDIO_TYPE_INPUT;                            /* Input type (recording device) */
475    caps.sub_type                = AUDIO_DSP_PARAM;                             /* Set all audio parameters */
476    caps.udata.config.samplerate = RECORD_SAMPLERATE;                           /* Sampling rate */
477    caps.udata.config.channels   = RECORD_CHANNEL;                              /* Channels */
478    caps.udata.config.samplebits = 16;                                          /* Bit depth */
479    rt_device_control(mic_dev, AUDIO_CTL_CONFIGURE, &caps);
480
481    while (1)
482    {
483        /* Read 20ms of audio data from the Audio device */
484        length = rt_device_read(mic_dev, 0, buffer, RECORD_CHUNK_SZ);
485
486        if (length)
487        {
488            /* Write the audio data to the file system */
489            write(fd, buffer, length);
490            total_length += length;
491        }
492
493        if ((total_length / RECORD_CHUNK_SZ) >  (RECORD_TIME_MS / 20))
494            break;
495    }
496
497    /* Rewrite the WAV file header */
498    wavheader_init(&header, RECORD_SAMPLERATE, RECORD_CHANNEL, total_length);
499    lseek(fd, 0, SEEK_SET);
500    write(fd, &header, sizeof(struct wav_header));
501    close(fd);
502
503    /* Close the Audio device */
504    rt_device_close(mic_dev);
505
506__exit:
507    if (fd >= 0)
508        close(fd);
509
510    if (buffer)
511        rt_free(buffer);
512
513    return 0;
514}
515MSH_CMD_EXPORT(wavrecord_sample, record voice to a wav file);
516```
517
518