EQ
EQ is an audio effect used to adjust the relative strength of different frequencies in an audio signal. It can control the frequency range of the audio, thereby affecting the overall sound quality and timbre. It can be used to enhance certain frequencies or attenuate unwanted frequencies, making the sound more balanced, clear, and layered. This document provides a comprehensive overview of EQ, focusing on its basic concepts, types, configuration methods, processes, and associated API.
EQ Types
As illustrated in the following figure, there are two types of EQ: SPK EQ and MIC EQ.

EQ Type
SPK EQ
When playing music or making a call, SPK EQ is used. It provides five modes, with the priority from high to low: Line-in EQ, voice SPK EQ, gaming EQ, ANC EQ, and normal EQ.
Each mode is specifically designed for its respective scenario:
Line-in EQ for Line-in audio.
Voice SPK EQ for voice.
Gaming EQ for gaming.
ANC EQ for active noise cancellation.
Normal EQ for initial mode.
MIC EQ
MIC EQ has two modes: APT EQ and voice MIC EQ.
EQ Configuration
EQ CFG mainly includes EQ enable, and user EQ.
EQ Enable
To use the EQ feature, it needs to be enabled first. This can be done using the DSPConfig Tool and the settings are saved in the DSP CFG image.
Taking audio SPK EQ as an example, as shown below:
Click Audio to enter the audio page.
Check Effect #0 to enable the effect.
Click Parametric EQ to open the settings page.

Audio SPK EQ Enable
On the setting page Audio SPK Parametric EQ Setting, users can set the specific parameters of SPK EQ, including global gain, EQ mode, EQ stage, etc.

Audio SPK EQ Setting
Here is a description of some parameters.
Global Gain
The settings for global gain.
Application
The options represent different EQ modes, namely normal EQ, gaming EQ, ANC EQ, and Line-in EQ. All settings are shared between these options except the IsEnable and IsDefault fields. It should be noted that voice SPK EQ is set in the Voice page.
StageNum
The number of stages for EQ, ranging from 0 to 10.
Increasing the number of stages will increase the size of the DSP CFG image. Each stage contains approximately 45 bytes.
IsEnable
Specifies the EQ index available in the current EQ mode.
For gaming, ANC, and Line-in EQ, there may be no active EQ index. In this case, the corresponding feature will be considered disabled.
IsDefault
Specifies which EQ index is the default value in the current EQ mode.
Freq, Gain, Q, and Type
The settings for the current EQ.
Total Response
The waveform of the current setting.
Exactly, the same logic applies to MIC EQ CFG as well. Take DSP APT EQ as an example, it needs to enable APT first. As shown below, the way to open the setting page is:
Click Audio to enter the audio page.
Select the Normal APT option to enable normal APT.
Click APT Process.
Click MIC EQ Settings.

APT EQ Enable
Same as audio SPK EQ, the Audio MIC Parametric EQ Setting is shown below.

APT EQ Setting
User EQ
User EQ refers to the EQ adjusted by phone. These EQs can be stored in SoC or phone, which is controlled by the F_APP_USER_EQ_SUPPORT
flag in the app_flags.h
file. When F_APP_USER_EQ_SUPPORT
is set to 1, these EQs can be stored in SoC. Otherwise, they are stored in the phone.
// app_flags.h
#define F_APP_USER_EQ_SUPPORT 1
Regarding the number of user EQs, different products are different. For RWS products, as shown in the figure below, they can be configured through MCUConfig Tool, including SPK and MIC EQ, with a range of 0 to 10.

User EQ CFG
While for TRX products, both the number of the SPK EQ and MIC EQ are fixed at 10 and cannot be configured.
void app_eq_cfg_init(void)
{
app_eq_cfg.user_eq_spk_eq_num = 10;
app_eq_cfg.user_eq_mic_eq_num = 10;
}
For example, if the number of the SPK EQ saved to flash is set to 10, all SPK EQs in the DSPConfig Tool will be stored in the SoC. And if the number of the SPK EQ saved to flash is set to 6 (less than 10), the first 6 groups of SPK EQs can be saved in the SoC, while the remaining 4 groups will be stored on the phone.
These user EQs will be saved to FTL, located in the EQ_FTL
module, with an allocation of EQ_SIZE
.
#define EQ_EXT_FTL_PARTITION_NAME "EQ_FTL"
#define AUDIO_AND_APT_EQ_SIZE (0x2800)
#define VOICE_SPK_EQ_SIZE (0x800)
#define EQ_SIZE (AUDIO_AND_APT_EQ_SIZE + VOICE_SPK_EQ_SIZE) // audio SPK EQ, APT EQ, voice SPK EQ
int32_t eq_ext_ftl_partition_init(const T_STORAGE_PARTITION_INFO *info)
{
return ftl_init_module(EQ_EXT_FTL_PARTITION_NAME, EQ_SIZE, EQ_EXT_FTL_BLOCK_LEN);
}
void eq_ext_ftl_storage_init(void)
{
static const T_STORAGE_PARTITION_INFO eq_partitions[] =
{
{
.name = EQ_EXT_FTL_PARTITION_NAME,
.init = eq_ext_ftl_partition_init,
...
},
};
storage_partition_init(eq_partitions, sizeof(eq_partitions) / sizeof(eq_partitions[0]));
}
One group of EQs has roughly the following structure, which includes:
EQ data (maximum 208 bytes): McuToSdkCmdHdr (4 bytes), public value (4 bytes), and stage parameter (StageNum * 5 * 4 bytes, StageNum is between 0 and 10). This part is used to send to DSP.
Byte 0~3 |
Byte 4~7 |
Byte 8~N |
---|---|---|
McuToSdkCmdHdr |
Public UInt32 Stage; //:4 Public Int32 GuadBit; //:4 Public Int32 Gain; //:24 |
Stage Parameter |
EQ information (maximum 54 bytes): EQ information header (4 bytes), and EQ information parameter (StageNum * 5 bytes, StageNum is between 0 and 10). This part is used to send to phone.
Byte 0~3 |
Byte 4~N |
---|---|
Public UInt16 StageNum; //:4 Public UInt16 SampleFreqIndex; //:4 SampleRate_T Public UInt16 Resv; //:8 Public Int16 GlobalGain; //:16 s5.10 |
Public UInt16 BiquadType; //:3 EQ_FILTER_TYPE_E Public UInt16 Q; //:10 Public Int16 Gain; //:12 Public Uint16 Freq; //15 |
Note
Different sample rates are handled differently. If the sample rate is 48k, the SoC does not need to report EQ information to the phone. Other sample rates (including 44.1K, 16K, 32K, etc.) must report EQ information.
For example, if StageNum is 5, the overall EQ size of this group that sent to DSP is about 108 bytes, which mainly includes McuToSdkCmdHdr (4 bytes), public value (4 bytes), and stage parameter (5 * 5 * 4 = 100 bytes). And for phone interaction, the EQ size is about 29 bytes, including EQ information header (4 bytes), and EQ information parameter (5 * 5 = 25 bytes).
EQ Process
In short, on the SoC side, the APP mainly uses the EQ module APIs of Audio Subsystem to transmit EQ parameters to the DSP, creating a specific sound effect for a given scenario. A complete EQ process should include: EQ start and stop, with optional functions like EQ change and EQ control as needed.
EQ Start and Stop
The EQ start process involves creating an initial EQ, enabling the EQ, and attaching the EQ. Conversely, the EQ stop process requires releasing the previously created EQ. As shown below, different EQs are slightly different.

SPK EQ Start and Stop

APT EQ Start and Stop
Taking audio SPK EQ as an example, the processing is as follows:
// audio SPK EQ start
1. audio_track_create()
2. app_eq_create()
|---app_eq_dsp_param_get()
|---eq_create()
3. eq_enable()
4. audio_track_effect_attach()
5. audio_track_start()
// audio SPK EQ stop
1. audio_track_release()
2. eq_release()
The step app_eq_create()
in the EQ start process is crucial. This step involves calling app_eq_dsp_param_get()
, which means that when APP creates an EQ, SoC will give priority to the user EQ stored in FTL. Only if the user EQ is unavailable will the default EQ in the DSP CFG image be used.
static uint16_t app_eq_dsp_param_get(T_EQ_TYPE eq_type, T_EQ_STREAM_TYPE stream_type,
uint8_t eq_mode, uint8_t index, void *data, uint16_t len, T_EQ_DATA_DEST eq_data_dest,
uint32_t sample_rate)
{
uint16_t eq_len = 0;
#if F_APP_USER_EQ_SUPPORT
if ((stream_type == EQ_STREAM_TYPE_AUDIO) || ((stream_type == EQ_STREAM_TYPE_VOICE) &&
(eq_type == SPK_SW_EQ)))
{
eq_len = app_eq_load_user_eq_from_ftl(eq_type, eq_mode, index, data, len, eq_data_dest,
sample_rate); // use EQ stored in FTL first
}
if (eq_len == 0)
#endif
{
eq_len = eq_utils_param_get(eq_type, stream_type, eq_mode, index, data, len, eq_data_dest,
sample_rate); // use EQ in the DSP CFG image second
}
...
return eq_len;
}
EQ Change
After the initial EQ is started, it can be modified as needed. There are two situations: EQ mode change and EQ index change.
EQ Mode Change
Changing the EQ mode is done automatically and changes both the current EQ mode and index. For example, when entering gaming mode, the SoC effect is switched from normal EQ to gaming EQ. When exiting gaming mode, the SoC effect returns to normal EQ.
It is implemented using the app_eq_change_audio_eq_mode
interface.
Judge whether the EQ mode needs to be changed, which follows the priority of SPK EQ mode.
Judge whether the EQ mode has changed. If so, update the EQ index and set it.
void app_eq_change_audio_eq_mode(bool report_when_eq_mode_change)
{
uint8_t new_eq_mode;
uint8_t eq_idx;
new_eq_mode = app_eq_judge_audio_eq_mode(); // judge whether the EQ mode needs to be changed
if (app_db.spk_eq_mode != new_eq_mode) // judge whether the EQ mode has changed
{
APP_PRINT_TRACE2("app_eq_change_audio_eq_mode: eq_mode %d -> %d", app_db.spk_eq_mode,
new_eq_mode);
app_db.spk_eq_mode = new_eq_mode;
app_eq_idx_check_accord_mode(); // update the EQ index
eq_idx = app_cfg_nv.eq_idx;
app_eq_index_set(SPK_SW_EQ, new_eq_mode, eq_idx); // set updated EQ index
#if F_APP_ERWS_SUPPORT
if (app_cfg_nv.bud_role != REMOTE_SESSION_ROLE_SECONDARY)
#endif
{
if (report_when_eq_mode_change)
{
app_report_eq_idx(EQ_INDEX_REPORT_BY_CHANGE_MODE);
}
}
}
}
EQ Index Change
Changing the EQ index only changes the EQ index, not the EQ mode. It is usually done by the EQ Control. For example, switching the index of the normal EQ from 0 to 1.
EQ Control
There are multiple ways to control EQ, including local control and remote control.
Local Control
Local control is only on the SoC side. Take the MMI as an example, if the current EQ mode contains more than one EQ, the EQ index can be switched. As shown below, MMI_AUDIO_EQ_SWITCH
is used to switch audio SPK EQ, and MMI_AUDIO_APT_EQ_SWITCH
is used to switch APT EQ.
MMI_AUDIO_EQ_SWITCH = 0x6B,
MMI_AUDIO_APT_EQ_SWITCH = 0x91,
Audio SPK EQ and APT EQ have similar EQ index switching processes, both use app_eq_index_set()
to update the relevant EQ index.
// MMI_AUDIO_EQ_SWITCH \ MMI_AUDIO_APT_EQ_SWITCH
app_eq_index_set()
|---app_eq_dsp_param_get()
|---eq_set()
Note
Since voice EQ only supports 1 group, including voice SPK EQ and voice MIC EQ, EQ index switching is not supported.
Remote Control
Remote control requires cooperation from the remote device. Take the phone tool (Audio Connect) as an example, before control, connection should be established for interaction between the SoC and phone.
The specific commands sent by Audio Connect will be processed in app_eq_cmd_handle()
on the SoC side.
typedef enum
{
CMD_AUDIO_EQ_QUERY = 0x0200,
...
CMD_AUDIO_EQ_PARAM_SET = 0x0203,
CMD_AUDIO_EQ_PARAM_GET = 0x0204,
CMD_AUDIO_EQ_INDEX_SET = 0x0205,
CMD_AUDIO_EQ_INDEX_GET = 0x0206,
...
CMD_APT_EQ_INDEX_SET = 0x020B,
CMD_APT_EQ_INDEX_GET = 0x020C,
...
CMD_AUDIO_EQ_QUERY_PARAM = 0x0210,
...
CMD_RESET_EQ_DATA = 0x0214,
} T_CMD_ID;
And after processing the command, the SoC will reply with the corresponding event to the Audio Connect by app_report_event()
.
typedef enum
{
EVENT_ACK = 0x0000,
...
EVENT_AUDIO_EQ_REPLY = 0x0200,
EVENT_AUDIO_EQ_PARAM_REPORT = 0x0201,
EVENT_AUDIO_EQ_INDEX_REPORT = 0x0202,
...
EVENT_APT_EQ_INDEX_INFO = 0x0206,
EVENT_AUDIO_EQ_REPLY_PARAM = 0x0208,
} T_EVENT_ID;
The following is an example of Audio Connect in action, using the RWS products as an example. Users can click Equalizer to control the audio SPK EQ, and click Real Hearing Enhancement to control the APT EQ.

Audio Connect Setting
As illustrated in the following figure, for audio SPK EQ:
Users can click the EQ below to switch to other EQ indexes.
Users can adjust the Gain of the current EQ, and other parameters such as Freq, Q, and Type are the same as the settings in the DSP CFG image.

Audio Connect Audio SPK EQ
As shown below, APT EQ can be controlled by clicking APT EQ on the Real Hearing Enhancement page.

Audio Connect Enter APT EQ
There are a few points to note:
The APT EQ is controlled the same way as the audio EQ.
If RWS separate adjust apt eq by audio connect is enabled in the MCUConfig Tool (only for RWS products), the Left and Right options will appear. This feature allows the APT EQ of each earbud to be adjusted separately using the Audio Connect.

Separate Adjust APT EQ CFG

Audio Connect APT EQ
EQ API
The EQ related APIs are as follows:
app_eq_init
Initialize the APP EQ module.
Usage example:
app_eq_init();
app_eq_deinit
Deinitialize the APP EQ module.
Usage example:
app_eq_deinit();
app_eq_create
Create EQ instance.
Usage example:
// SPK EQ p_link->eq_instance = app_eq_create(EQ_CONTENT_TYPE_AUDIO, EQ_STREAM_TYPE_AUDIO, SPK_SW_EQ, app_db.spk_eq_mode, app_cfg_nv.eq_idx); p_link->voice_spk_eq_instance = app_eq_create(EQ_CONTENT_TYPE_VOICE, EQ_STREAM_TYPE_VOICE, SPK_SW_EQ, app_db.spk_eq_mode, app_cfg_nv.eq_idx); // MIC EQ app_db.apt_eq_instance = app_eq_create(EQ_CONTENT_TYPE_PASSTHROUGH, EQ_STREAM_TYPE_AUDIO, MIC_SW_EQ, APT_MODE, app_cfg_nv.apt_eq_idx); p_link->voice_mic_eq_instance = app_eq_create(EQ_CONTENT_TYPE_VOICE, EQ_STREAM_TYPE_VOICE, MIC_SW_EQ, VOICE_MIC_MODE, app_cfg_nv.eq_idx);
app_eq_index_set
Set the specific EQ index.
Usage example:
// SPK EQ app_eq_index_set(SPK_SW_EQ, app_db.spk_eq_mode, app_cfg_nv.eq_idx); // APT EQ app_eq_index_set(MIC_SW_EQ, APT_MODE, app_cfg_nv.apt_eq_idx);
app_eq_dsp_param_get
Get specific EQ parameter.
Usage example:
// get the EQ parameter sent to DSP eq_len = app_eq_dsp_param_get(eq_type, stream_type, mode, index, dynamic_eq_buf, app_db.max_eq_len, EQ_DATA_TO_DSP, sample_rate); // get the EQ parameter sent to source eq_len = app_eq_dsp_param_get((T_EQ_TYPE)p_eq_info->sw_eq_type, stream_type, p_eq_info->eq_mode, p_eq_info->eq_idx, eq_data_temp, app_db.max_eq_len, EQ_DATA_TO_PHONE, sample_rate);
app_eq_param_set
Set the specific EQ index parameter.
Usage example:
app_eq_param_set(eq_mode, eq_idx, &cmd_ptr[0], eq_len_to_dsp);
app_eq_play_audio_eq_tone
Play audio EQ tone.
Usage example:
app_eq_play_audio_eq_tone();
app_eq_play_apt_eq_tone
Play APT EQ tone.
Usage example:
app_eq_play_apt_eq_tone();
app_eq_report_eq_param
Report EQ parameter.
Usage example:
app_eq_report_eq_param(cmd_path, app_idx);
app_eq_report_terminate_param_report
Terminate report EQ parameter.
Usage example:
app_eq_report_terminate_param_report(cmd_path, app_idx);
app_eq_sample_rate_get
Get current sample rate.
Usage example:
app_eq_sample_rate_get();
app_eq_idx_check_accord_mode
Check idx accord EQ mode.
Usage example:
app_eq_idx_check_accord_mode();
app_eq_sync_idx_accord_eq_mode
Check EQ mode then sync idx.
Usage example:
app_eq_sync_idx_accord_eq_mode(eq_mode, eq_idx);
app_eq_cmd_handle
EQ related command handler.
Usage example:
app_eq_cmd_handle(cmd_ptr, cmd_len, cmd_path, app_idx, ack_pkt);
app_eq_audio_eq_enable
Enable the EQ effect.
Usage example:
app_eq_audio_eq_enable(&p_link->eq_instance, &p_link->audio_eq_enabled);
app_eq_change_audio_eq_mode
Change audio speaker EQ mode.
Usage example:
app_eq_change_audio_eq_mode(true);
app_eq_judge_audio_eq_mode
Judge audio speaker EQ mode.
Usage example:
app_db.spk_eq_mode = app_eq_judge_audio_eq_mode();
app_eq_save_user_eq_to_ftl
Save specific user EQ to FTL.
Usage example:
app_eq_save_user_eq_to_ftl((T_EQ_TYPE)app_db.sw_eq_type, eq_mode, eq_idx, eq_data[0], eq_adjust_side, &eq_data[1], eq_len_to_dsp, &eq_data[1 + eq_len_to_dsp], p_audio_eq_info->eq_data_len - eq_len_to_dsp - 1);
app_eq_is_valid_user_eq_index
Check if the specific EQ is a valid user EQ or not.
Usage example:
#if (F_APP_USER_EQ_SUPPORT == 1) if (app_eq_is_valid_user_eq_index(SPK_SW_EQ, app_db.spk_eq_mode, app_cfg_nv.eq_idx)) { app_report_eq_idx(EQ_INDEX_REPORT_BY_GET_UNSAVED_EQ); } else #endif { app_report_eq_idx(EQ_INDEX_REPORT_BY_PLAYING); }
app_eq_reset_user_eq
Reset specific user EQ.
Usage example:
app_eq_reset_user_eq((T_EQ_TYPE)eq_type, eq_mode, eq_idx);
app_eq_reset_all_user_eq
Reset all user EQ.
Usage example:
app_eq_reset_all_user_eq();
app_eq_reset_unsaved_user_eq
Reset unsaved user EQ.
Usage example:
app_eq_reset_unsaved_user_eq();
app_eq_sync_user_eq_when_connected
Sync user EQ when buds are connected.
Usage example:
app_eq_sync_user_eq_when_connected();
app_eq_continue_sync_user_eq_when_connected
Continue syncing user EQ when buds are connected.
Usage example:
app_eq_continue_sync_user_eq_when_connected(false, eq_type, eq_index, offset, len - 5);
app_eq_process_sync_user_eq_when_b2b_connected
Process syncing user EQ message when buds are connected.
Usage example:
app_eq_process_sync_user_eq_when_b2b_connected(eq_type, eq_index, offset, &buf[5], len - 5);
app_eq_report_sec_eq_to_pri
Report the EQ of secondary to primary.
Usage example:
app_eq_report_sec_eq_to_pri(eq_type, eq_mode, eq_index, cmd_path, app_idx);
app_eq_report_sec_eq_to_src
Report the EQ of secondary to source.
Usage example:
app_eq_report_sec_eq_to_src(cmd_path, app_idx, eq_len, &buf[7]);