オーディオの基礎と Esp-sr 入門ガイドの簡単な紹介

このブログでは、最初にオーディオの基本について簡単に説明し、その後、読者が esp-sr SDK を使い始めるのを支援します。

1 オーディオの基本概念

1.1 音の性質

音の本質は媒体中の波の伝播現象であり、音波の本質は波、つまり物理量です。この 2 つは異なり、音は抽象化された音波伝播現象であり、音波は物理量です。

1.2 音の三要素

  • ラウドネス:人が主観的に知覚する音のサイズ (一般に音量として知られています)。これは、振幅と人と音源の間の距離によって決まります。
  • ピッチ: 周波数の差によって音のピッチ (高音、低音) が決まります。周波数が高いほど、ピッチも高くなります (周波数の単位は Hz、ヘルツ)。人間の可聴範囲は 20 ~ 20000 Hz です。20 Hz 以下は超低周波と呼ばれ、20000 Hz 以上は超音波と呼ばれます。
  • 音色:物体や素材の特性により、音には異なる特性があり、音色自体は抽象的なものですが、波形はこの抽象的で直感的な演奏です。波形はピッチごとに異なり、波形によって異なるピッチを区別できます。

画像

1.3 デジタルオーディオのいくつかの基本概念

1.3.1 サンプリング

いわゆるサンプリングとは、信号を時間軸上のみデジタル化することです。

  • ナイキストの法則 (サンプリングの法則とも呼ばれます) によれば、サンプリングは音の最高周波数の 2 倍で実行されます。人間の可聴周波数(ピッチ)範囲は 20 Hz ~ 20 KHz です。したがって、少なくとも 40 kHz より大きくなります。サンプリング周波数は通常 44.1 kHz で、最大 20 kHz のサウンドもデジタル化できることが保証されます。44.1 kHz は 1 秒あたり 44100 サンプルを意味します。

Espressif AI 音声のサンプリング レートは 16 kHz です。サンプリング周波数 16 kHz の半分は、人間の音声で一般的に使用される周波数帯域の上限である約 8 kHz に相当します。また、サンプリング レート 44.1 kHz は、もう 1 つの一般的に使用されるサンプリング レート 44.1 kHz 同じ時間長でも、サンプリング レートが高いほどデータ量も多くなります。 通常、インスタント メッセージングのオーディオでは、信号送信の適時性を確保するために 16 kHz またはそれより低いサンプリング レートが使用されますが、オーディオ品質に影響を与える 特定の損失 (鈍い音など) を引き起こす; 高品質サウンドに重点を置いたオーディオ リソースを録音する場合、再生信号の高忠実度を確保するために、より多くのコストをかけて 44.1 kHz または 48 kHz のサンプリング レートを使用することになります。データストレージ。

画像
したがって、この部分には主に次の 3 つのパラメータが含まれます。

  • ビット レート: ビット レートは、1 秒あたりに送信されるビット数です。単位はビット (bps ビット/秒) です。
  • サンプリング: サンプリングとは、連続時間信号を離散デジタル信号に変換することです。
  • サンプリング レート: サンプリング レートは、1 秒あたりに取得されるサンプルの数です。

1.3.2 量子化

量子化とは、振幅軸上の信号のデジタル化を指します。サンプルが 16 ビットのバイナリ信号で表される場合、サンプルで表される範囲は [-32768, 32767] です。

Espressif AI 音声は16ビット量子化を使用します。

1.3.3 チャンネル数

チャンネル数とは音のチャンネル数のことで、モノラル、バイノーラル、ステレオが一般的です。

  • モノラルサウンドは 1 つのスピーカーからのみ出力することも、2 つのスピーカーに処理して同じチャンネルのサウンドを出力することもできますが、モノラル情報を 2 つのスピーカーで再生すると、その音が 2 つのスピーカーから出ているとはっきりと感じることができます。音源が 2 つのスピーカーの間で耳に伝わるかどうか、音源の特定の位置を特定します。

  • バイノーラルとは音のチャンネルが2つあることを意味し、その原理は人が音を聞いたとき、左耳と右耳の位相差によって音源の特定の位置を判断できるというものです。録音中にサウンドが 2 つの別々のチャンネルに分割されたため、優れた音の定位が得られました。

1.3.4 音声サイズの計算

例: 時間 1 秒、サンプリング レート 16000 Hz、サンプリング サイズ 16、チャネル番号 2 のオーディオを録音するには、占有スペースは次のようになります: 16000 * 16 * 2 * 1 s= 500k

2 音響フロントエンド (オーディオ フロントエンド、AFE)

Espressif AFE アルゴリズム フレームワークのセットは、強力な ESP32 および ESP32-S3 SoC に基づく音響フロントエンド処理に使用でき、ユーザーは高品質で安定したオーディオ データを取得できるため、優れたパフォーマンスと優れたパフォーマンスを備えたインテリジェントな音声製品を構築できます。高いコストパフォーマンス。

2.1 音響エコーキャンセレーション (AEC)

音響エコー キャンセル アルゴリズムは、マイクから音声が入力されるときに適応フィルターを使用してエコーを除去します。このアルゴリズムは、スピーカーを通じてオーディオを再生する音声デバイスなどのシナリオに適しています。

このアルゴリズムは最大でもデュアル マイク処理をサポートしており、マイク入力信​​号内の自動再生サウンドを効果的に除去できます。これにより、単体で音楽を再生しながら音声認識などのアプリケーションを良好に実行することができます。

2.2 ブラインドソース分離 (BSS)

ブラインド ソース分離アルゴリズムは、複数のマイクを使用して入力音声の方向を検出し、特定の方向からの音声入力を強調します。このアルゴリズムにより、騒がしい環境でも目的のオーディオ ソースの音質が向上します。

2.3 ノイズサプレッション(NS)

ノイズ抑制アルゴリズムはシングルチャンネルオーディオ信号処理をサポートしており、人間以外の無駄な音 (掃除機やエアコンなど) を効果的に除去し、処理されるオーディオ信号を改善します。

Espressif AFE がサポートする 3 つのシナリオ

Espressif AFE の機能は、次の 2 つの異なるシナリオを対象としています。

  1. 音声認識シーン

  2. 音声通話シーン

3.1 音声認識のシナリオ

モデルの手順:

  1. オーディオ入力

  2. エコー キャンセル用の AEC (エコー チャネルを必要とする独自の音声アナウンスを削除します)

    • ハード コールバック: IIS を介してスピーカーに書き込まれたデータを直接読み取ります (1 つの IIS をマイクと共有できます)
    • ソフト コールバック: スピーカーに書き込まれるソフトウェア コピー データ (未サポート、開発待ち)
  3. BSS/NS

    • BSS (ブラインド ソース セパレーション) アルゴリズムはデュアル チャネル処理をサポートしており、目的の音源を他の干渉音から盲目的に分離することで、有用なオーディオ信号を抽出し、その後の音声の品質を保証します。
    • NS (ノイズ抑制) アルゴリズムはシングル チャネル処理をサポートしており、シングル チャネル オーディオ内の非人間の音声ノイズ、特に定常状態のノイズを抑制でき、優れた抑制効果があります。
    • どのアルゴリズムを使用するかは、構成されたマイクの数によって異なります。
    • VAD (音声アクティビティ検出) アルゴリズムは、現在のフレームの音声アクティビティ ステータスのリアルタイム出力をサポートします。
  4. ウェイクネット

    ウェイクワード

対応するフローチャートは次のとおりです。
画像の説明を追加してください

3.2 音声通話のシナリオ

モデルの手順:

  1. オーディオ入力
  2. エコー キャンセル用の AEC (エコー チャネルを必要とする独自の音声アナウンスを削除します)
    • ハード コールバック: IIS を介してスピーカーに書き込まれたデータを直接読み取ります (1 つの IIS をマイクと共有できます)
    • ソフト コールバック: スピーカーに書き込まれるソフトウェア コピー データ (未サポート、開発待ち)
  3. BSS/NS
    • BSS (ブラインド ソース セパレーション) アルゴリズムはデュアル チャネル処理をサポートしており、目的の音源を他の干渉音から盲目的に分離することで、有用なオーディオ信号を抽出し、その後の音声の品質を保証します。
    • NS (ノイズ抑制) アルゴリズムはシングル チャネル処理をサポートしており、シングル チャネル オーディオ内の非人間の音声ノイズ、特に定常状態のノイズを抑制でき、優れた抑制効果があります。
    • どのアルゴリズムを使用するかは、構成されたマイクの数によって異なります。
  4. 味噌
    • MISO (Multi Input Single Output) アルゴリズムは、デュアルチャンネル入力とシングルチャンネル出力をサポートします。ウェイクアップが有効になっていないデュアル マイク シナリオで、高い S/N 比を持つオーディオ出力を選択するために使用されます。
  5. AGC
    • AGC (Automatic Gain Control) は出力音声の振幅を動的に調整し、弱い信号が入力されると出力振幅を増幅し、入力信号が一定の強度に達すると出力振幅を圧縮します。

対応するフローチャートは次のとおりです。
画像の説明を追加してください

3.3 設定コードのリファレンス

#define AFE_CONFIG_DEFAULT() {
      
       \
    .aec_init = true, \                      	     	//AEC 算法是否使能
    .se_init = true, \									//BSS/NS 算法是否使能
    .vad_init = true, \									//VAD 是否使能 ( 仅可在语音识别场景中使用 )
    .wakenet_init = true, \								//唤醒是否使能.
    .voice_communication_init = false, \				//语音通话是否使能。与 wakenet_init 不能同时使能.
    .voice_communication_agc_init = false, \        	//语音通话中AGC是否使能
    .voice_communication_agc_gain = 15, \               //AGC的增益值,单位为dB
    .vad_mode = VAD_MODE_3, \                      	    //VAD 检测的操作模式,越大越激进
    .wakenet_model_name = NULL, \                       //选择唤醒词模型
    .wakenet_mode = DET_MODE_2CH_90, \              	//唤醒的模式。对应为多少通道的唤醒,根据mic通道的数量选择
    .afe_mode = SR_MODE_LOW_COST, \						//SR_MODE_LOW_COST: 量化版本,占用资源较少。 
        												//SR_MODE_HIGH_PERF: 非量化版本,占用资源较多。
    .afe_perferred_core = 0, \                      	//AFE 内部 BSS/NS/MISO 算法,运行在哪个 CPU 核
    .afe_perferred_priority = 5, \                  	//AFE 内部 BSS/NS/MISO 算法,运行的task优先级。
    .afe_ringbuf_size = 50, \                       	//内部 ringbuf 大小的配置
    .memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM, \	//绝大部分从外部psram分配
    .agc_mode = AFE_MN_PEAK_AGC_MODE_2, \               //线性放大喂给后续multinet的音频,峰值处为 -4dB。
    .pcm_config.total_ch_num = 3, \                     //total_ch_num = mic_num + ref_num
    .pcm_config.mic_num = 2, \							//音频的麦克风通道数。目前仅支持配置为 1 或 2。
    .pcm_config.ref_num = 1, \                          //音频的参考回路通道数,目前仅支持配置为 0 或 1。
}

4 AI音声モデル

4.1 ウェイクネット

4.1.1 menuconfig によるモデルの選択

wn9_hiesp (最新の wn9 はデフォルトの 8 ビット量子化です): バージョン 9、ウェイク ワードは hi、esp です

画像の説明を追加してください

4.2 マルチネット

4.2.1 menuconfig によるモデルの選択

mn4q8_cn : バージョン 4、8 ビット量子化、中国語コマンドワード
画像の説明を追加してください

4.3 コマンドワードの追加

4.3.1 menuconfig を通じてコマンドワードを追加する

  • 中国語のコマンド単語に直接ピンインを追加します: エアコンをオンにします (da kai kong tiao)。また、同じコマンド ID、最大風速/最大風速をサポートするために複数の文もサポートします。

    方言コマンド単語を追加: 対応する発音を追加します。
    画像の説明を追加してください

  • 英語のコマンド単語には、Python スクリプトを通じて生成される対応する音素を追加する必要があります。
    画像の説明を追加してください

4.3.2 コード内にコマンドワードを動的に追加する

esp_mn_commands_add(i, token);

APIを呼び出してコマンドワードを動的に追加します。

アルゴリズムのパフォーマンス

CPU、30 KB SRAM、および 500 KB PSRAM をわずか約 20% しか消費しません

5 マイクの設計

5.1 マイクの推奨性能

  1. マイクの種類: 無指向性 MEMS マイク。

  2. 感度:

    • 1 Pa の音圧以下では、アナログ感度は -38 dBV 未満であってはならず、デジタル感度は -26 dB 未満であってはなりません。
    • 許容誤差は±2 dBで制御され、マイクアレイには±1 dBが推奨されます
  3. SNR

    S/N 比は 62 dB 以上であり、64 dB 以上が推奨されます。

    信号対雑音比が高いほど、サウンドの忠実度が高くなります。

    • 周波数特性:50~16kHzの範囲で周波数特性の変動は±3dB以内
    • 電源除去比 (PSRR): n >55 dB(MEMS MIC)

6 構造設計の提案

  1. マイクと構造コンポーネントの共振周波数が確実に 1 mm 以上になるように、マイクの穴の直径または幅は 1 mm より大きく、ピックアップ パイプはできるだけ短く、キャビティはできるだけ小さくすることをお勧めします。 9KHz。

  2. ピックアップ穴の深さと直径の比は 2:1 未満で、シェルの厚さは 1 mm が推奨されますが、シェルが厚すぎる場合は穴の面積を大きくする必要があります。

  3. ラックの穴は防塵ネットで保護する必要があります。

  4. 密閉性と耐衝撃性を確保するために、マイクとデバイスのシェルの間にシリコン スリーブまたはフォームを追加する必要があり、マイクの気密性を確保するには締まりばめ設計が必要です。

  5. マイク穴を塞ぐことはできません。テーブルによってマイク穴が塞がれないように、下部のマイク穴を構造上で高くする必要があります。

  6. マイクは、スピーカーやノイズや振動を発生するその他の物体から離して配置し、ゴムパッドでスピーカーの空洞から隔離して緩衝する必要があります。

7 コードの説明 (CN_SPEECH_COMMANDS_RECOGNITION)

7.1 ヘッダーファイル

#include "esp_wn_iface.h"                   //唤醒词模型的一系列API
#include "esp_wn_models.h"					//根据输入的模型名称得到具体的唤醒词模型
#include "esp_afe_sr_iface.h"				//语音识别的音频前端算法的一系列API
#include "esp_afe_sr_models.h"              //语音前端模型的声明
#include "esp_mn_iface.h"                   //命令词模型的一系列API
#include "esp_mn_models.h"                  //命令词模型的声明
#include "esp_board_init.h"                 //开发板硬件初始化
#include "driver/i2s.h"                     //i2s 驱动
#include "speech_commands_action.h"         //根据识别到的 command 进行语音播报/闪烁 LED
#include "model_path.h"                     //从 spiffs 文件管理中返回模型路径等 API

7.2 アプリメイン

void app_main()
{
    
    
    models = esp_srmodel_init("model");                                //spiffs 中的所有可用模型或  model 默认是从`flash`读
    ESP_ERROR_CHECK(esp_board_init(AUDIO_HAL_08K_SAMPLES, 1, 16));     //Special config for dev board   
    // ESP_ERROR_CHECK(esp_sdcard_init("/sdcard", 10));                //初始化 SD card
#if defined CONFIG_ESP32_KORVO_V1_1_BOARD
    led_init();                                                        //LED 初始化
#endif

    afe_handle = &ESP_AFE_SR_HANDLE;                                   
    afe_config_t afe_config = AFE_CONFIG_DEFAULT();					   //音频前端的配置项

    afe_config.wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);;  //从有所可用的模型中找到唤醒词模型的名字
#if defined CONFIG_ESP32_S3_BOX_BOARD || defined CONFIG_ESP32_S3_EYE_BOARD
    afe_config.aec_init = false;
#endif
    //afe_config.aec_init = false;                                       //关闭 AEC
    //afe_config.se_init = false;                                        //关闭 SE
    //afe_config.vad_init = false;                                       //关闭VAD
    //afe_config.pcm_config.total_ch_num = 2;                            //设置为单麦单回采
    //afe_config.pcm_config.mic_num = 1;                                 //麦克风通道一
    esp_afe_sr_data_t *afe_data = afe_handle->create_from_config(&afe_config);

    xTaskCreatePinnedToCore(&feed_Task, "feed", 4 * 1024, (void*)afe_data, 5, NULL, 0);        //feed 从 i2s 拿到音频数据
    xTaskCreatePinnedToCore(&detect_Task, "detect", 8 * 1024, (void*)afe_data, 5, NULL, 1);    //将音频数据喂给模型获取检测结果

#if defined  CONFIG_ESP32_S3_KORVO_1_V4_0_BOARD || defined CONFIG_ESP32_KORVO_V1_1_BOARD
    xTaskCreatePinnedToCore(&led_Task, "led", 2 * 1024, NULL, 5, NULL, 0);                     //开启LED
#endif
#if defined  CONFIG_ESP32_S3_KORVO_1_V4_0_BOARD || CONFIG_ESP32_S3_KORVO_2_V3_0_BOARD || CONFIG_ESP32_KORVO_V1_1_BOARD
    xTaskCreatePinnedToCore(&play_music, "play", 2 * 1024, NULL, 5, NULL, 1);                  //开启语音播报
#endif
}

7.2 フィード動作

void feed_Task(void *arg)
{
    
    
    esp_afe_sr_data_t *afe_data = arg;
    int audio_chunksize = afe_handle->get_feed_chunksize(afe_data);
    int nch = afe_handle->get_channel_num(afe_data);
    int feed_channel = esp_get_feed_channel();         //3;
    int16_t *i2s_buff = malloc(audio_chunksize * sizeof(int16_t) * feed_channel);
    assert(i2s_buff);
    size_t bytes_read;

    while (1) {
    
    
        //第一种方式 
        //audio_chunksize:音频时间 512->32ms 256->16ms
        //int16_t:16位量化
        //feed_channel:两麦克风通道数据一回采通道数据
        esp_get_feed_data(i2s_buff, audio_chunksize * sizeof(int16_t) * feed_channel);
        //第二种方式
        i2s_read(I2S_NUM_1, i2s_buff, audio_chunksize * sizeof(int16_t) * feed_channel, &bytes_read, portMAX_DELAY);
        afe_handle->feed(afe_data, i2s_buff);
    }
    afe_handle->destroy(afe_data);
    vTaskDelete(NULL);
}

7.3 検出動作

void detect_Task(void *arg)
{
    
    
	esp_afe_sr_data_t *afe_data = arg;
    int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data);
    int nch = afe_handle->get_channel_num(afe_data);
    char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_CHINESE);       //从模型队列中获取命令词模型名字
    printf("multinet:%s\n", mn_name);
    esp_mn_iface_t *multinet = esp_mn_handle_from_name(mn_name);                     //获取命令词模型
    model_iface_data_t *model_data = multinet->create(mn_name, 5760);                //创建
    esp_mn_commands_update_from_sdkconfig(multinet, model_data); 					 // Add speech commands from sdkconfig
    int mu_chunksize = multinet->get_samp_chunksize(model_data);
    int chunk_num = multinet->get_samp_chunknum(model_data);
    assert(mu_chunksize == afe_chunksize);
    printf("------------detect start------------\n");
    // FILE *fp = fopen("/sdcard/out1", "w");
    // if (fp == NULL) printf("can not open file\n");
    while (1) {
    
    
        afe_fetch_result_t* res = afe_handle->fetch(afe_data);                       //获得AEF的处理结果
        if (!res || res->ret_value == ESP_FAIL) {
    
    
            printf("fetch error!\n");
            break;
        }
#if CONFIG_IDF_TARGET_ESP32
        if (res->wakeup_state == WAKENET_DETECTED) {
    
                                    
            printf("wakeword detected\n");
            play_voice = -1;
            detect_flag = 1;
            afe_handle->disable_wakenet(afe_data);
            printf("-----------listening-----------\n");
        }
#elif CONFIG_IDF_TARGET_ESP32S3
        if (res->wakeup_state == WAKENET_DETECTED) {
    
                              
            printf("WAKEWORD DETECTED\n");                                           //如果被唤醒将唤醒标志置位True
        } else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED) {
    
    
            play_voice = -1;
            detect_flag = 1;
            printf("AFE_FETCH_CHANNEL_VERIFIED, channel index: %d\n", res->trigger_channel_id);
        }
#endif

        if (detect_flag == 1) {
    
    
            esp_mn_state_t mn_state = multinet->detect(model_data, res->data);       //将AFE处理后的音频数据给命令词模型

            if (mn_state == ESP_MN_STATE_DETECTING) {
    
    
                continue;
            }

            if (mn_state == ESP_MN_STATE_DETECTED) {
    
    
                esp_mn_results_t *mn_result = multinet->get_results(model_data);    //得到结果
                for (int i = 0; i < mn_result->num; i++) {
    
    
                    printf("TOP %d, command_id: %d, phrase_id: %d, prob: %f\n", 
                    i+1, mn_result->command_id[i], mn_result->phrase_id[i], mn_result->prob[i]);
                }
                printf("\n-----------listening-----------\n");
            }

            if (mn_state == ESP_MN_STATE_TIMEOUT) {
    
                                     //超时关闭
                afe_handle->enable_wakenet(afe_data);
                detect_flag = 0;
                printf("\n-----------awaits to be waken up-----------\n");
                continue;
            }
        }
    }
    afe_handle->destroy(afe_data);
    vTaskDelete(NULL);
}

8 Espressif AI 関連の Github リファレンス

おすすめ

転載: blog.csdn.net/Marchtwentytwo/article/details/129370026