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