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

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

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.

EQ Data

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.

EQ Information

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.

../../../_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, 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.

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