ESP Codec Device
General Information
esp_codec_dev is a driver component for audio codec devices. Following features are supported currently:
- Support driver for common audio codec devices
- Support multiple instance of codec devices (including device of same type)
- Add unified abstract interface to operate on codec device
- Support customized codec realization based on provided interface
- Easy-to-use high-level API for playback and recording
- Support for volume adjustment in software when it is not supported in hardware
- Support customized volume curve and customized volume control
- Easy to port to other platform after replacing codes under platform
The currently supported codec devices are listed as below:
| Playback | Record | |
|---|---|---|
| ES8311 | Y | Y |
| ES8388 | Y | Y |
| ES8374 | Y | Y |
| ZL38063 | Y | Y |
| TAS6805M | Y | N |
| AW88298 | Y | N |
| ES7210 | N | Y |
| ES7243 | N | Y |
| ES7243E | N | Y |
| ES8156 | N | Y |
Architecture overview
Hardware connection and software architecture are introduced respectively by taking the codec device (ES8311) as an example.
The hardware connection diagram between the codec device (ES8311) and the main IC (ESP32-S3) is as below:
graph LR;
subgraph Mic
Microphone
end
subgraph ESP32-S3
I2C_Bus
I2S_Bus
PA_GPIO
end
subgraph ES8311
ADC
DAC
I2C_Port
I2S_Port
end
subgraph NS4150
IN
CTRL
OUT
end
subgraph "Left Speaker"
Speaker
end
I2C_Bus --> I2C_Port
I2S_Bus --> I2S_Port
DAC --> IN
OUT --> Speaker
PA_GPIO --> CTRL
Microphone --> ADC
ESP32-S3 sends control data to ES8311 through I2C bus and exchanges audio data through I2S bus. During playing, ES8311 receives digital audio data from I2S bus and performs DAC operation, then the analog signal is amplified by PA chip (NS4150) and finally is output through the speaker. During recording, ES8311 gets the analog signal from the microphone, amplifies it, and performs ADC operation, then digital audio data can be obtained from ESP32-S3.
Communication between ESP32-S3 and ES8311 mainly occurs on two paths:
- Control path: Set up the codec chip (using I2C bus)
- Data path: Exchange audio data (using I2S bus)
In software architecture, the above hardware behavior is abstracted as:
classDiagram
direction LR;
class audio_codec_ctrl_if_t {
open()
read_reg()
write_reg()
close()
}
class audio_codec_gpio_if_t {
setup()
set()
get()
}
class es8311_codec_cfg_t {
audio_codec_ctrl_if_t *ctrl_if
audio_codec_gpio_if_t *gpio_if
int16_t pa_pin
esp_codec_dev_hw_gain_t hw_gain
}
class audio_codec_if_t {
audio_codec_ctrl_if_t* ctrl_if
open()
enable()
set_fs()
set_vol()
set_mic_gain()
close()
}
class audio_codec_data_if_t {
open()
set_fmt()
read()
write()
close()
}
class esp_codec_dev {
audio_codec_data_if_t* data_if
audio_codec_if_t* codec_if
esp_codec_dev_new()
esp_codec_dev_open()
esp_codec_dev_read()
esp_codec_dev_write()
esp_codec_dev_set_out_vol()
esp_codec_dev_set_in_gain()
esp_codec_dev_set_vol_curve()
esp_codec_dev_set_vol_handler()
esp_codec_dev_close()
}
audio_codec_ctrl_if_t ..> es8311_codec_cfg_t
audio_codec_gpio_if_t ..> es8311_codec_cfg_t
es8311_codec_cfg_t ..> audio_codec_if_t
audio_codec_if_t ..> esp_codec_dev
audio_codec_data_if_t ..> esp_codec_dev
esp_codec_dev abstracts the above communication path into two interfaces:
audio_codec_ctrl_if_tfor the control path:
The control interface mainly offersread_regandwrite_regAPIs to do codec setup
Commonly used control channels include I2C, SPI, etcaudio_codec_data_if_tfor data path:
The data interface mainly offersreadandwriteAPIs to exchange audio data
Commonly used data channels include I2S, SPI, etc
esp_codec_dev provides users with convenient high-level API to implement playback and recording functions. It is composed of audio_codec_data_if_t and audio_codec_if_t. audio_codec_if_t abstracts codec control operations and constructed by specified codec configuration (configured by audio_codec_ctrl_if_t and audio_codec_gpio_if_t through es8311_codec_cfg_t). audio_codec_gpio_if_t abstracts the IO control to adapt to the main control IO or the expansion chip IO, and called inside the codec to match the unique set timing.
DAC Volume setting
Volume setting is realized by common API: esp_codec_dev_set_out_vol.
esp_codec_dev supports the following volume setup methods:
- Use codec register to adjust the volume
- Use built-in software volume
audio_codec_new_sw_volwhen codec hardware does not support volume adjustment - Use customized software volume interface through
esp_codec_dev_set_vol_handler
The default volume range is 0 - 100. Volume [1:100] is mapped to [-49.5 dB:0 dB] with the scale being 0.5 dB. Volume 0 is mapped to -96 dB. To change this mapping, you can define your own volume curve through API esp_codec_dev_set_vol_curve. The volume curve is an array of esp_codec_dev_vol_map_t which uses linear interpolation to calculate the decibel value at a certain volume point internally (please sort volume maps in advance).
To balance the speaker's loudness across different platforms when playing the same content, you need to know some mechanism of the audio gain. In short, audio gain consists of two parts: software gain (adjustable) and hardware gain (fixed). Software gain can be adjusted by changing the input PCM data level or setting the codec volume register. The hardware gain is affected by the peripheral circuit, mainly by the amplification efficiency of the analog signal. The typical impact parameter of hardware gain is extracted into esp_codec_dev_hw_gain_t, which can be configured to codec devices to ensure loudness consistency. For more details, please refer to the comments in esp_codec_dev_vol.h.
Usage
The steps below take the ES8311 codec as an example to illustrate how to play and record audio.
-
Install the driver for codec control and data bus referring to test_board.c
ut_i2c_init(0); ut_i2s_init(0); -
Create the control and data interfaces for the codec using the interface provided by default
audio_codec_i2s_cfg_t i2s_cfg = { #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) .rx_handle = i2s_keep[0]->rx_handle, .tx_handle = i2s_keep[0]->tx_handle, #endif }; const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg); audio_codec_i2c_cfg_t i2c_cfg = {.addr = ES8311_CODEC_DEFAULT_ADDR}; const audio_codec_ctrl_if_t *out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio(); -
Create the codec interface based on control interface and codec-specified configuration
es8311_codec_cfg_t es8311_cfg = { .codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH, .ctrl_if = out_ctrl_if, .gpio_if = gpio_if, .pa_pin = YOUR_PA_GPIO, .use_mclk = true, }; const audio_codec_if_t *out_codec_if = es8311_codec_new(&es8311_cfg); -
Get
esp_codec_dev_handle_tthroughesp_codec_dev_new
Now you can use the handle for further playback and recording as follows:esp_codec_dev_cfg_t dev_cfg = { .codec_if = out_codec_if; // codec interface from es8311_codec_new .data_if = data_if; // data interface from audio_codec_new_i2s_data .dev_type = ESP_CODEC_DEV_TYPE_IN_OUT; // codec support both playback and record }; esp_codec_dev_handle_t codec_dev = esp_codec_dev_new(&dev_cfg); // Below code shows how to play esp_codec_dev_set_out_vol(codec_dev, 60.0); esp_codec_dev_sample_info_t fs = { .sample_rate = 48000, .channel = 2, .bits_per_sample = 16, }; esp_codec_dev_open(codec_dev, &fs); uint8_t data[256]; esp_codec_dev_write(codec_dev, data, sizeof(data)); // Below code shows how to record esp_codec_dev_set_in_gain(codec_dev, 30.0); esp_codec_dev_read(codec_dev, data, sizeof(data)); esp_codec_dev_close(codec_dev);
How to customize for new codec device
-
Implement
audio_codec_ctrl_if_tandaudio_codec_data_if_t
If you are using I2C bus for control and I2S bus for data, you can use the implementation provided by default:
audio_codec_new_i2c_ctrlandaudio_codec_new_i2s_data -
Implement
audio_codec_if_tbased on the interface built in step 1typedef struct { const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */ const audio_codec_gpio_if_t *gpio_if; /*!< If you want to operate GPIO */ //................................... Other settings } my_codec_cfg_t; const audio_codec_if_t *my_codec_new(my_codec_cfg_t *codec_cfg);
For details, refer to the sample code my_codec.c.