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.

../../../_images/eq_type.png

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.

  • APT EQ corresponds to DSP APT EQ, while LLAPT does not support EQ function.

  • Voice MIC EQ is also used in voice scenarios, like voice SPK 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:

  1. Click Audio to enter the audio page.

  2. Check Effect #0 to enable the effect.

  3. Click Parametric EQ to open the settings page.

../../../_images/dsp_cfg_enable_spk_eq.png

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.

../../../_images/dsp_cfg_setting_spk_eq.png

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 10 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 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:

  1. Click Audio to enter the audio page.

  2. Select the Normal APT option to enable normal APT.

  3. Click APT Process.

  4. Click MIC EQ Settings.

../../../_images/dsp_cfg_open_apt_eq_setting.png

APT EQ Enable

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

../../../_images/dsp_cfg_setting_apt_eq.png

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.

../../../_images/mcu_cfg_user_eq.png

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]));
}

These user EQs are stored as follows:

  • 1 header, structured as T_EQ_USER_EQ_HEADER.

  • N audio SPK EQs, and N is the number of SPK EQs. Each is structured as T_EQ_USER_EQ_DATA.

  • N APT EQs, and N is the number of MIC EQs. Each is structured as T_EQ_USER_EQ_DATA.

  • 1 voice SPK EQ, structured as T_EQ_USER_EQ_DATA. Since voice SPK EQ only supports 1 group, its EQ index is fixed at 0.

typedef struct t_eq_user_eq_header
{
   uint8_t spk_user_eq_num; // audio SPK EQ num, except voice SPK EQ
   uint8_t mic_user_eq_num; // APT EQ num, except voice MIC EQ
   uint8_t user_eq_version;
   uint8_t rsv[1];
} T_EQ_USER_EQ_HEADER;

typedef struct t_eq_user_eq_data
{
   uint16_t eq_data_len;
   uint8_t  eq_type;
   uint8_t  eq_idx;
   uint8_t  eq_data[318];
   uint8_t  rsv[2];
} T_EQ_USER_EQ_DATA;

The actual EQ data in T_EQ_USER_EQ_DATA has a maximum length of 318 bytes (3 * 106 bytes), including final EQ parameters, UI EQ parameters, and compensation EQ parameters. Each EQ parameter (up to 106 bytes) follows the structure below:

  • Type (1 byte): This indicates the type of EQ parameters and may be one of the following:

    • Final type (0x00) is applied to DSP.

    • UI type (0x01) is used for synchronization with the phone.

    • Compensation type (0x02) represents compensation EQ parameters.

  • Length (1 byte): Specifies the length of the payload.

  • Payload (up to 104 bytes), as shown in the following figure EQ Payload:

    • Header (4 bytes).

    • Coefficient (up to 100 bytes): Consisting of StageNum * 10 bytes, where StageNum ranges from 0 to 10.

typedef enum
{
   EQ_PARAM_TYPE_FINAL         = 0x00,
   EQ_PARAM_TYPE_UI            = 0x01,
   EQ_PARAM_TYPE_COMPENSATION  = 0x02,
   EQ_PARAM_TYPE_MAX           = 0x03,
} T_EQ_PARAM_TYPE;

typedef struct t_EQ_PARAM
{
   uint8_t type;
   uint8_t length;
   uint8_t payload[0];
} T_EQ_PARAM;
../../../_images/eq_struct.png

EQ Payload

For example, if StageNum is 5, the total EQ size (only payload of the final type) sent to the DSP is 54 bytes, which includes the EQ header (4 bytes), and the EQ coefficients (5 * 10 = 50 bytes). The total EQ size sent to the phone is 112 bytes, including the final type and UI type, 56 bytes each. Additionally, the type and length fields are also required.

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.

../../../_images/spk_eq_start_stop.png

SPK EQ Start and Stop

../../../_images/apt_eq_start_stop.png

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, uint8_t *data,
                                     bool is_eq_payload)
{
   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, is_eq_payload); // use EQ stored in FTL first
   }

   if (eq_len == 0)
#endif
   {
      // 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.

  1. Judge whether the EQ mode needs to be changed, which follows the priority of SPK EQ mode.

  2. 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, a 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.

../../../_images/audioconnect_setting.png

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.

../../../_images/audioconnect_audio_eq.png

Audio Connect Audio SPK EQ

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

../../../_images/audioconnect_enter_apt_eq_page.png

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.

../../../_images/mcu_cfg_separate_adjust_apt_eq.png

Separate Adjust APT EQ CFG

../../../_images/audioconnect_apt_eq.png

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,
                                    true);
      
      // 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, false);
      
  • 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_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_adjust_side,
                                 &eq_data[0],
                                 p_audio_eq_info->eq_data_len);
      
  • 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]);