Module-LLMのマイクを調べてみた件

## 目的
Module-LLMのマイクにて録音を行い、wavファイルのデータ評価を行いました。

## 注意事項
Module-LLMの初期ファームウェアで評価した結果です。今後のファームウェアバージョンアップで修正が入る可能性があります。
audioデータは、AXERA-TECHのSDKを使ってデータ取得することが推奨されています。
Pythonのプログラムでは、AXERA-TECH SDK対応のaudioライブラリは現状用意されていないため、Linuxの標準環境であるALSAを使って録音する手段を主に評価しました。


## 結論

~~現状、ALSAを経由してマイクからのデータを扱う場合には、BitRate設定：24Bitもしくは32Bitで取り込み、16BitのBitRateへ変換することがお勧めとなります。また、AXERA-TECHのSDKを使って、マイクから録音する手段がお勧めとなります。~~

@ciniml 氏により、原因が特定されました。

@ciniml / Module LLMの音声入力まわり
https://zenn.dev/ciniml/articles/module_llm_audio_system

LLM_buildroot-external-m5stack/issue/Wrong audio samples via ALSA with 16bit bits per sample mode #3
https://github.com/m5stack/LLM_buildroot-external-m5stack/issues/3

## 背景

Module-LLMの発売直後、、@devemin 氏と@tokkyo氏 によって、マイクから音声がとれたと報告と、設定によってマイクの音が変であるという報告がありました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">録音は、<br>cd /root<br>arecord -D plughw:0,0 -f S24_LE -r 24000 mic2.wav<br><br>再生は、<br>aplay -D plughw:0,1 /root/mic2.wav<br><br>でうまくできた。<br><br>録音のオプションの Hz / bitrate の組合わせによって、ノイズのでる変な音になる。組み合わせによって正しく録音される？<br>-f S16_LE or 未指定 (bitrate)<br>-r…</p>&mdash; でべ (@devemin) <a href="https://twitter.com/devemin/status/1853944346556719523?ref_src=twsrc%5Etfw">November 5, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">いろいろALSAを試した感じ、S16_LEのときは指定した2倍のデータをsnd_pcm_readi()で取得してしまっているような感じっぽいな👀 (S32_LEのときはOK)</p>&mdash; tokkyo (@tokkyo) <a href="https://twitter.com/tokkyo/status/1862601497294594408?ref_src=twsrc%5Etfw">November 29, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

その後、 @mongonta555　氏によって、ffmpegでの変換を組み合わせることによって、
日本語認識できるwavファイルが生成できたと報告がありました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">・arecordで5秒録音<br>・ファイルヘッダを32000Hzに書き換え<br>・ffmpegで32000HzのWAVファイルを16000Hzに変換<br>・Juliusでファイルを読み込んで音声認識<br><br>という手順です。<a href="https://twitter.com/hashtag/M5StackLLM?src=hash&amp;ref_src=twsrc%5Etfw">#M5StackLLM</a> <a href="https://twitter.com/hashtag/M5Stack?src=hash&amp;ref_src=twsrc%5Etfw">#M5Stack</a></p>&mdash; タカヲ(Takao) (@mongonta555) <a href="https://twitter.com/mongonta555/status/1862873233189679501?ref_src=twsrc%5Etfw">November 30, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 調査内容
これまでのXでの経緯を踏まえて、ALSA経由のマイクに不具合がありそうだということで、スマートフォンから、toneジェネレータのアプリを使って、1kHzの音をModule-LLMの近くで鳴らしてマイクから録音してみました。


![image](https://github.com/user-attachments/assets/0c9a1dc2-8ea6-404e-99d6-70cc1248d696)

Module-LLMで、ALSA経由でマイクから音を1秒取得するPythonのプログラムを起動しました。
音のデータをwavファイルに保存しました。

事前に、alsa-utils,portaudio,pyaudioをインストールします。

```
root@m5stack-LLM:# sudo apt install alsa-utils portaudio19-dev
root@m5stack-LLM:# pip install pyaudio
```
```
root@m5stack-LLM:# python3 mic_input_data.py
```


```python3 mic_input_data.py
if __name__ == "__main__":
#Bitrate 16Bit
    record_audio("S16_LE_08000Hz.wav", duration=1,format_bit=pyaudio.paInt16,sample_rate=8000)
    record_audio("S16_LE_16000Hz.wav", duration=1,format_bit=pyaudio.paInt16,sample_rate=16000)
    record_audio("S16_LE_24000Hz.wav", duration=1,format_bit=pyaudio.paInt16,sample_rate=24000)
    record_audio("S16_LE_32000Hz.wav", duration=1,format_bit=pyaudio.paInt16,sample_rate=32000)
#Bitrate 32Bit
    record_audio("S32_LE_08000Hz.wav", duration=1,format_bit=pyaudio.paInt32,sample_rate=8000)
    record_audio("S32_LE_16000Hz.wav", duration=1,format_bit=pyaudio.paInt32,sample_rate=16000)
    record_audio("S32_LE_24000Hz.wav", duration=1,format_bit=pyaudio.paInt32,sample_rate=24000)
    record_audio("S32_LE_32000Hz.wav", duration=1,format_bit=pyaudio.paInt32,sample_rate=32000)
#Bitrate 24Bit
    record_audio("S24_LE_08000Hz.wav", duration=1,format_bit=pyaudio.paInt24,sample_rate=8000)
    record_audio("S24_LE_16000Hz.wav", duration=1,format_bit=pyaudio.paInt24,sample_rate=16000)
    record_audio("S24_LE_24000Hz.wav", duration=1,format_bit=pyaudio.paInt24,sample_rate=24000)
    record_audio("S24_LE_32000Hz.wav", duration=1,format_bit=pyaudio.paInt24,sample_rate=32000)
```
ソースコードの全文は以下です。
https://github.com/nnn112358/py_mic_test<br>

次に、wavファイルをテキストファイルに変換するpythonプログラムを実行しました。

```
root@m5stack-LLM:# python3 wav_to_csv.py
```

```wav_to_csv
if __name__ == "__main__":
    wav_to_csv("S16_LE_08000Hz.wav", "S16_LE_08000Hz.csv")
    wav_to_csv("S16_LE_16000Hz.wav", "S16_LE_16000Hz.csv")
    wav_to_csv("S16_LE_24000Hz.wav", "S16_LE_24000Hz.csv")
    wav_to_csv("S16_LE_32000Hz.wav", "S16_LE_32000Hz.csv")
    wav_to_csv("S24_LE_08000Hz.wav", "S24_LE_08000Hz.csv")
    wav_to_csv("S24_LE_16000Hz.wav", "S24_LE_16000Hz.csv")
    wav_to_csv("S24_LE_24000Hz.wav", "S24_LE_24000Hz.csv")
    wav_to_csv("S32_LE_08000Hz.wav", "S32_LE_08000Hz.csv")
    wav_to_csv("S32_LE_16000Hz.wav", "S32_LE_16000Hz.csv")
    wav_to_csv("S32_LE_24000Hz.wav", "S32_LE_24000Hz.csv")
    wav_to_csv("S32_LE_32000Hz.wav", "S32_LE_32000Hz.csv")
```
ソースコードの全文は以下です。
https://github.com/nnn112358/M5_LLM_Module_Report/blob/main/audio/wav_to_csv.py<br>


## 調査結果

テキスト化したwavファイルのデータをグラフ化したものが以下になります。
32Bitと24Bitのwavファイルのデータは、16Bitのwavファイルにあわせるように振幅を換算する後処理を入れています。具体的にはpythonプログラムを参照してください。

![image](https://github.com/user-attachments/assets/009342e7-a3a2-4024-9e9c-bbb81017d818)

## 考察

・BirRate：16Bitでは、1kHzの音が、500Hzに変化しています。１個のデータを採取するごとに、振幅 0 のデータが挿入されています。16Bitごとのデータをやり取りするべきところを、誤って32Bitずつやり取りしているなどの不具合がある可能性があります。
・32Bitと24Bitのwavはそのような異常が見られないので、32Bitもしくは24Bitで録音するのがよいと考えられます。

## 次の評価
Module-LLMのマイクでは、LinuxのALSA Driverを経由して録音を行うと、16Bitのときに異常が見られました。
そこで、次は推奨されているAXERA-TECHのSDKを使って、マイクから録音する手段を試してみました。



## sample_audioでの録音

Module-LLMでは、sample_audioコマンドを使って、AXERA-TECHのSDKを使ったマイクの操作を行うことができます。
sample_audioは、AXERA-TECHが提供しているSDK(ax620e_bsp_sdk)のサンプルプログラムです。

Module-LLMで、以下のコマンドを打つことでマイクから録音することができます。

```
# 16Bit 8kHzで録音
root@m5stack-LLM:# sample_audio ai -D 0 -d 0 -r 8000 -p 160 -w 1 -o sample_audio_S16_LE_08000Hz.wav 
# 16Bit 16kHzで録音
root@m5stack-LLM:# sample_audio ai -D 0 -d 0 -r 16000 -p 160 -w 1 -o sample_audio_S16_LE_16000Hz.wav
# 16Bit 24kHzで録音
root@m5stack-LLM:# sample_audio ai -D 0 -d 0 -r 24000 -p 160 -w 1 -o sample_audio_S16_LE_24000Hz.wav
# 16Bit 32kHzで録音
root@m5stack-LLM:# sample_audio ai -D 0 -d 0 -r 32000 -p 160 -w 1 -o sample_audio_S16_LE_32000Hz.wav
```

Module-LLMのマイクを調べてみた件(ALSA編)と同じく、
Module-LLMで、スマホからの1kHzのトーン音を録音しました。
こちらのプログラムからは、16Bitで取り込んでも目立った異常はないことを確認しました。

 <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/3657c7dd-700d-da7d-804d-9504b241c9fa.png" width=30%>


## sample audioの説明

こちらにある、sample audioのドキュメントを翻訳しました。
https://github.com/AXERA-TECH/ax620e_bsp_sdk/tree/main/msp/sample/audio


1）機能の説明:：
audioフォルダーの下にあるコードは、顧客がオーディオ モジュール全体の構成プロセスをすぐに理解できるように、AXERA-TECHの SDK パッケージによって提供されるサンプル リファレンス コードです。サンプル コードでは、ai 記録、ao 再生、ai_aenc エンコード、adec_ao デコードの関数を示します。AXERA-TECHは、Everest メーカーのコーデック ドライバー( es8388、es7210、es8311、es7243l、および es8156)に対応しています。

2）使用例：
例 1: ヘルプ情報を表示する
````
sample_audio -h
````
例 2: 16kHz オーディオを録音する
````
sample_audio ai -D 0 -d 0 -r 16000 -p 160 -w 1
````
例 3: FIXED モードのエコー キャンセルを有効にして 16kHz オーディオを録音する
````
sample_audio ai -D 0 -d 0 -r 16000 -p 160 --aec-mode 2 --routing-mode 0 --layout 1 -w 1
````
例 4: 16kHz オーディオを再生する
````
sample_audio ao -D 0 -d 1 -r 16000
````
例 5: 16kHz オーディオを録音してエンコードする
````
sample_audio ai_aenc -D 0 -d 0 -r 16000 -p 160 -w 1
````
例 6: 16kHz オーディオを録音し、モノラルでエンコードする
````
sample_audio ai_aenc -D 0 -d 0 -r 16000 -p 160 --layout 1 --aenc-chns 1 -w 1
````
例 7: 16kHz オーディオをデコードして再生する
````
sample_audio adec_ao -D 0 -d 1 -r 16000
````
例 8: 内蔵コーデック アップストリーム HPF LPF EQ
````
sample_audio ai -D 0- d 0 --hpf 1 --hpf-freq 200 --hpf-db -12 --lpf 1 --lpf-freq 3000 --lpf-db 0 --eq 1
````
例 9: 内蔵コーデック ダウンストリーム HPF LPF EQ
````
sample_audio ao -D 0 -d 1 --hpf 1 --hpf-freq 200 --hpf-db -12 --lpf 1 --lpf-freq 3000 --lpf-db 0 --eq 1
````
3) 実行結果の例:
正常に実行したら、Ctrl+C を実行して終了します。wave ファイルが現在のディレクトリに生成され、ユーザーはそれを開いて実際の効果を確認できます。

4) 注意事項:
(1) サウンド カード番号とデバイス番号については、/dev/snd/ を参照してください。例:
 pcmC0D0c: カード 0、デバイス 0、キャプチャ デバイス
 pcmC0D1p: カード 0、デバイス 1、再生デバイス


## 謝辞
情報を頂いた、@devemin氏 @tokkyo氏、@mongonta555氏に感謝を申し上げます m(_ _)m

## 参考リンク
https://x.com/nnn112358/status/1863508092471930989
https://x.com/devemin/status/1853944346556719523
https://x.com/HanxiaoM/status/1853866167137644827
https://x.com/HanxiaoM/status/1862455344288735508
https://x.com/mongonta555/status/1862872794406773242
https://wiki.onakasuita.org/pukiwiki/?Module%20LLM%2FJulius
https://x.com/tokkyo/status/1862655788202041414
https://x.com/nnn112358/status/1862636059571708066