chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

お越しいただきありがとうございます。

このブログでは主に以下の内容を記載しています(自分の記録用)。

  • 何か作った話(自分は何かを作るために生きている..できたらクスっと笑えるものや、アートと絡みたい)(何かに手を出しても続かず、飽きるのが問題)
  • 音楽(AerophoneやDTM(FL-Studio))を勉強しようと思ったが、音楽はレッスンが続けられないので、、絵に転向しようとしたが、DTMに戻った(が、悶々とするだけで何も作っていない)
  • 趣味のロードバイク(坂道をひーこら走ると結構大変な人生(笑)と重なる。。その後、定年・再雇用で仕事のストレスが軽いのでほとんど走っていない。。いかん)

記事が渾然一体となってカオス状態ですが、マイコンのプログラムも、ちゃりで走るのも、料理するのも、自分の中ではそれぞれが繋がっているので、カオスな日常をそのまま反映させています。リンクや引用はご自由にどうぞ。質問等ありましたらコメントに書いてください。微力ながら自分の分かる範囲で回答します。
とにかく、、人に「あほやなぁ」と言われる事に精進するのが我が人生。へこんでも創造の力で進みたい
■2023年修正
当初は「あほ道」を進む予定だったが、「あほ道」は情熱と強い信念がないと進めない。自分は歳と共に情熱が落ちてきた。残りの人生は「ワクワク」や「面白い」ものを追いかけたい。ワクワク+面白体験に人生残りの時間とお金をかける*1
定年再雇用後はこれまでの仕事を少し減らして、プログラミング学習の支援活動を行う予定(愛想悪くて生徒様からチェンジ要望が出されるかもしれないが)
■2024年目標再設定
これまで仕事とか趣味はデジタル分野であった。アナログ回路とか電磁気は全く分かっていない。残りの人生は、微積電磁気学、ベクトル解析?を学んで、音、音楽、波形、FFT、アナログ回路、フィルタ回路、アンプ設計等、アナログ方面の理解と作成に取り組みたい。それに、、(来るか来ないか分からいけど)量子コンピューティングは量子の挙動を行列で表現して演算するらしいし。。ちょうどいい。

*1:ちなみに7月で定年・再雇用

動作を確認するため、RPiにWebIOPiをインストールする

背景:WebIOPiについて異常動作の相談を受けたので自分のRPiに入れて動作を確認する
異常動作:daemon起動にするとWebIOPiのloop関数が正常に動作しない
結論:既知の不具合のようで、workaroundでの回避手段が必要
お詫び:編集でミスしたようで、記事の途中に宣伝が入っています。どうやって消したらいいのか分からずご了承ください

詳細:

  1. packageをDLする
  2. Patchをあてる
  3. setup.shでインストール
sudo ./setup.sh
Do you want to access WebIOPi over Internet ? [y/n]
n

WebIOPi successfully installed
* To start WebIOPi foreground   : sudo webiopi [-h] [-c config] [-l log] [-s script] [-d] [port]
* To start WebIOPi background   : sudo /etc/init.d/webiopi start
* To start WebIOPi at boot      : sudo update-rc.d webiopi defaults
* Run ./weaved-setup.bin to install the Weaved IoT Kit and access your device over Internet
* Look in /home/sumi/lang/py/webiopi/WebIOPi-0.7.1/examples for Python library usage examples
  1. Service登録用の設定ファイルをDLして、所定のフォルダに配置(service 登録)
/etc/systemd/system/webiopi.service
  1. service コマンドを用いてwebiopiを起動
$ sudo service webiopi start
$ ps aux | grep webiopi
root        2036  1.8  0.2 170568 17060 ?        Ssl  17:09   0:00 /usr/bin/python3 -m webiopi -l /var/log/webiopi -c /etc/webiopi/config

オリジナルのHTML/Scriptを導入するには以下の手順
Tutorial : Framework basis

  1. 自動起動設定
 sudo systemctl enable webiopi
# service webiopi
Usage: /etc/init.d/webiopi {start|stop|status|restart|force-reload}
root@raspberrypi:~# service webiopi status
● webiopi.service - WebIOPI
     Loaded: loaded (/etc/systemd/system/webiopi.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2024-08-18 17:38:56 JST; 3min 24s ago
   Main PID: 516 (python3)
      Tasks: 3 (limit: 8755)
        CPU: 965ms
     CGroup: /system.slice/webiopi.service
             └─516 /usr/bin/python3 -m webiopi -l /var/log/webiopi -c /etc/webiopi/config

確かに、コマンドラインからWebIOPiを起動するとloopは動作するが、daemon起動させるとloopが正しく動かないようだ。この件はすでにQAがあって、それによると、起動スクリプトにまずい所がありそうで setupの関数内でsleepを入れて起動を遅らせろという回避策が紹介されていた。
[Scripts] in Config not working if Webiopi started with systemctl · Issue #46 · thortex/rpi3-webiopi · GitHub


■追記
ログを有効にする方法

設定ファイルの編集: WebIOPiの設定ファイル(通常は/etc/webiopi/config)を開きます。以下のように、ログファイルのパスを指定します。
[LOG]
file = /var/log/webiopi.log

サービスファイルの確認: WebIOPiのサービスファイル(通常は/etc/systemd/system/webiopi.service)を確認し、ExecStart行にログファイルのパスを追加します。
[Service]
ExecStart=/usr/bin/python3 -m webiopi -l /var/log/webiopi.log -c /etc/webiopi/config

■追記
WebIOPiのファイル類が置かれている場所

$ pwd
/usr/local/lib/python3.9/dist-packages/WebIOPi-0.7.1-py3.9-linux-aarch64.egg/webiopi
$ ls
__init__.py  __pycache__  decorators  protocols  utils
__main__.py  clients      devices     server

■参考にした記事
Raspberry Piに「WebIOPi」を導入する - nyabot’s diary
[Scripts] in Config not working if Webiopi started with systemctl · Issue #46 · thortex/rpi3-webiopi · GitHub
Raspberry Piでプログラムを自動起動する5種類の方法を比較・解説 #RaspberryPi - Qiita
JavaScriptでcookieを削除する方法 | UX MILK
GitHub - thortex/rpi3-webiopi: WebIOPi for Raspberry Pi 1, 2, 3, and zero

ヨメ不在でカレーを作る(サバカレー)(再)

背景:香りがきついから自宅でカレーを作るのを禁止されている
取り組み:今日はヨメがいない→(家族分の)カレーを作る
結果:突き抜けるほどのスパイス感はないけど、普通に食べられるカレーができた
詳細:
材料は以下(4人分)
スパイス:
・クミンシード:小2
コリアンダー:小さじ4
ターメリック:大2.5(多すぎないか? *1
・カイエンペッパー:小さじ1/2
ニンニク、しょうが:少々
玉ねぎ:1.5 *2
トマト:ピューレで代用
オイル:大3(当初大4で作ったけど、大3で良いと思う)
鯖缶:1缶
コンブをつけておいた水(400cc)
材料(玉ねぎ、トマトピューレ、サバ缶(減塩版))

スパイスは、クミン、カルダモン、ターメリック(カイエンペッパーは非常に良く効くので取り扱い注意)
写真のターメリックは間違って大匙で計ったので量が多すぎる。

まずはクミンをいためる(写真はオイル大匙4ですが、これだと多すぎて、大匙3で十分)

玉ねぎは一玉と半玉を追加(こんなに入れなくてもよかったのではと反省、甘くなりすぎるというか、厚みが出てしまってさらっとした味にならないというか)

玉ねぎが色づいたのでトマトピューレを投入、さらに煮込む

ペースト状になったので、パウダースパイスを投入

コンブだしを投入

サバの水煮を煮汁と共に投入

トマトの角切りを投入(酸味を加えたい気持ち)

サーバカレー完成

スパイスが突き抜けるという感じはなくて、出汁の効いたまったりしたサバカレーになった。自分でスパイスをつぶしてオリジナルのガラムマサラを作って足したいところ
■参考URL
自家製ガラムマサラを作って香り高いカレーをモノにする!(プロの配合・レシピ付き) | 7ページ目 (7ページ中) | buono

*1:大匙と小さじを間違っていると思われる

*2:多すぎると、味が分厚くなってスパイスが薄まる感じがするので、1玉で十分ではなかろうか。スパイスが主役になるよう材料は減らしていきたい

memo: DTFT/DFT

【理工学部講義】物理情報数学C (フーリエ ラプラス Z変換) - YouTube
https://youtu.be/xvX8aaok0PE
https://www.youtube.com/watch?v=COooukoXqZQ

www.udemy.com
(そもそも英語がよく分かっていないが、講師がインドの方のようで、さらに難解)

Raspberry Pi PicoのPIOプログラミング(I2Sインターフェースの実装) #RaspberryPi - Qiita

コードのデータを作ってI2Sで鳴らしてみる

やりたい事:440HzのSin波を作って鳴らしてみたけど、スピーカの特性によるのか矩形波のようなBEEP音になっていた。試しにコードの波形を作って鳴らしてみる
どうせならマイナーな、ラドミの和音の波形を作ってみる。
できたこと:和音の波形を作ってI2S制御のスピーカから再生した。
課題:和音が再生されたがノイズがひどい。また、単音の場合もi2s.write()でデータを送信する都度音が途切れる。callback方式で実装しても音が途切れる。どうやったら途切れることなく連続再生できるのか不明(Git上のサンプルコードを調べると何かわかるかも)*1
まとめ:やっぱり音のプログラムは難しい。MicroPython等のインタプリタで制御するにはハード側でcacheを持たせる等いろいろ工夫しないと、音が途切れる症状が出ると思われる。

  周波数        1周期にかかるclock数(44.1KHz)
--------------------------------------
# ラ A3 220.000      200 clock
# ド C4 261.626      169 clock
# ミ E4 329.628      134 clock

ラドミの和音による波形データを作るプログラムは以下

from math import pi
from math import sin
from machine import I2S
from machine import Pin

# code (A3_C4_E4)
# A3 220.000      200 clock
# C4 261.626      169 clock
# E4 329.628      134 clock

A3_CYCLE = 200
C4_CYCLE = 169
E4_CYCLE = 134


MAX_VOLUME = 0x7fff
volume = int(MAX_VOLUME * 0.5)


def dump2Bls(buf, len=128, onHex=False):
  for i in range(len):
     data = buf[i*2+1] << 8
     data += buf[i*2]
     if onHex:
         print(f'{data:02x} ',end='')
     if data >= 0x8000:
         print(data - 0x10000)
     else:
         print(data)

def toI16(data):
   if data == 0:
      lb = 0
      ub = 0
   elif (data > 0) and (data <= 0x7fff):
      lb = data & 0xff
      ub = (data >> 8) & 0xff
   elif (data < 0) and  (data >= (0x8000 * -1)):
      data = 0x10000 + data
      lb = data & 0xff
      ub = (data >> 8) & 0xff
   else:      
      lb = None
      ub = None
   return (ub, lb)		      


i2s = I2S(0, sck=Pin(18), ws=Pin(19), sd=Pin(20), mode=I2S.TX, bits=16, format=I2S.MONO, rate=44100, ibuf=20_000)

buffer_size = 44100 * 2              # *2 means 2byte(16bit)  and 1sec
buf = bytearray(buffer_size)         # 

for i in range(int(len(buf)/2)):
    a3 = sin( 2 * pi * i / A3_CYCLE)
    c4 = sin( 2 * pi * i / C4_CYCLE)
    e4 = sin( 2 * pi * i / E4_CYCLE)
    tone = int(volume * (a3 + c4 + e4 ) / 2)
    i16 = toI16(tone)
    buf[i * 2] = i16[1]
    buf[i * 2 + 1] = i16[0]

while True:
    i2s.write(buf)

ノイズがひどくて聞くに堪えない。なぜノイズが乗るのか分からない。和音の作り方がまずいのかも。Excelで波形を描画させたが特に非連続な部分は見つけられず

*1:WAVフォーマットに仕上げてPCで再生させることにより、データの問題か再生装置の問題かを切り分けられそうだ

I2S接続のSpeaker(M5 STACK社のSpeaker2 Hat (MAX98357))をMicroPython/I2Sで制御して鳴らす

本来はMEMS MICを正しく実装したいのだが、デバッグ等でI2Sバスで音を確認できるといろいろ助かる。また、I2Sで扱うデータ仕様の理解も深まる。
そこで、I2S接続のSpeakerを波形データを作って鳴らしてみる。使ったパーツは、M5 STACK社のSpeaker2 Hat (MAX98357)

作るデータ:440HzのSin波
データ仕様は、16bit、44.1KHzサンプリング、Monoral
高速化等を無視してmath.sin関数でただただ計算する。符号付きint16変換がちょっと怪しいがプログラムは以下
バッファサイズとして、 buffer_size = 44100 * 2  としたので、1秒間鳴るはず (I2Sバスに対して、44.1KHzサンプリング指定) 。

#
# make sin wave
#

import math
from machine import I2S, Pin

def dump2Bls(buf, len=128, onHex=False):
  for i in range(len):
     data = buf[i*2+1] << 8
     data += buf[i*2]
     if onHex:
         print(f'{data:02x} ',end='')
     if data >= 0x8000:
         print(data - 0x10000)
     else:
         print(data)

def toI16(data):
   if data == 0:
      lb = 0
      ub = 0
   elif (data > 0) and (data <= 0x7fff):
      lb = data & 0xff
      ub = (data >> 8) & 0xff
   elif (data < 0) and  (data >= (0x8000 * -1)):
      data = 0x10000 + data
      lb = data & 0xff
      ub = (data >> 8) & 0xff
   else:      
      lb = None
      ub = None
   return (ub, lb)		      

buffer_size = 44100 * 2              # *2 means 2byte(16bit)
buf = bytearray(buffer_size)

max = 0x7fff

for i in range(int(len(buf)/2)):
    rad = math.pi * 2 * i / 100
    data = max * math.sin(rad)
    i16 = toI16(int(data))
    #print(data, i16)
    buf[i * 2] = i16[1]
    buf[i * 2 + 1] = i16[0]

i2s = I2S(0, sck=Pin(18), ws=Pin(19), sd=Pin(20), mode=I2S.TX, bits=16, format=I2S.MONO, rate=44100, ibuf=20000)

i2s.write(buf)

波形データが正しいか、グラフ化して確認

プログラムを実行すると、M5のSpeakerから音は鳴った。ただ、、Sin波のようなこもった音ではなく、矩形波のような尖ったBEEP音であった。これはスピーカが小さいせいか?

■追記
I2Sのバス仕様、波形の信号はMSBから送信される仕様のようだ。

https://www.nxp.com/docs/en/user-manual/UM11732.pdf
音声データのファイルフォーマットとして、マルチバイトの表現は下位バイトから並べるらしい。

WAVファイルのフォーマットについて | Ingenious

■バイトオーダを確認する
I2Sに渡すバッファ内はL_ch→R_ch→L_ch→Rの順番で並べて(モノラルの場合は、L_ch→L_ch→L_ch→L_ch)、各Channnelは下位バイト→上位バイトの順番で並べると理解した。本当にそれで合っているのかを確認
以下のような固定波形を繰り返し出力するプログラムを作成

#
# byte order check
#

import math
from machine import I2S, Pin

def dump(buf,len=4):
  for i in range(len):
     print(f'{buf[i]:02x} ',end='')
     if (i+1) % 16 == 0:
        print('')


buf = bytes((
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
0x45, 0xA1, 0x09, 0xED,0x45, 0xA1, 0x09, 0xED,
))

i2s = I2S(0, sck=Pin(18), ws=Pin(19), sd=Pin(20), mode=I2S.TX, bits=16, format=I2S.MONO, rate=44100, ibuf=20000)

while True:
   i2s.write(buf)
   print('.')

バッファ内が、0x45,0xA1なので、I2Sのバスを観察すると、0xA145のデータがMSBから送信されるはず。オシロで確認するとそのようになってる。

L_ch -> R_chの確認もすべきかもだけど、片側のChannelしか使わないので、省略(出力バスを見ていると、モノラル出力指定したら、L_chの値と同じ値がR_chに出力されているようだ、。)

■追記
MicroPythonのDocumentにバッファのバイトオーダはLittle Endianであると書かれていた。なるほど。
クラス I2S -- IC間サウンド(Inter-IC Sound)バスプロトコル — MicroPython latest ドキュメント

I2S接続のMEMS MIC (MSM261S4030H0R )を使って録音してみる→かなり感度が悪い

やりたいこと:I2S仕様のMEMS MICをRPi Pico(RP2040)版MicroPythonから制御
8/11時点の結論:MicroPythonのI2Sライブラリを使ってバッファにデータ蓄積することはできた
課題:かなり感度が悪い。これでは録音というレベルではない。

MSM261S4030H0R 
Fermion: I2S MEMS Microphone Sensor (Breakout) - DFRobot
Fermion - I2S MEMS マイク — スイッチサイエンス
https://www.denovocn.com/sites/default/files/MSM261S4030H0R.pdf

RP2040/uPythonのI2S 仕様
RP2 用クイックリファレンス — MicroPython 1.19.1 ドキュメント

RPiとI2Sで接続してデータを取ってみる。I2Sの設定は以下のように、16bit長、44.1KHzサンプリング、内蔵バッファは20KB、Monoral、

i2s = I2S(0, sck=Pin(18), ws=Pin(19), sd=Pin(20), mode=I2S.RX, bits=16, format=I2S.MONO, rate=44100, ibuf=20000)

使ったI2Sバス接続のMEMS MICは MSM261S4030H0R で、LR指定をLに落としてLeftを出力するように指定。この時の波形は以下

斜めに下がってる所がかなり気になるが、、ここは読まれない領域と勝手に解釈して、i2sで取り込んだはずのバッファをダンプすると以下

for i in range(0x100):
  print(f'{buf[i]:02x} ',end='')
  if (i+1) % 16 == 0:
     print('')


11 00 02 00 00 00 0d 00 09 00 05 00 02 00 04 00
02 00 ff ff 0d 00 08 00 fe ff 0a 00 04 00 04 00
02 00 06 00 0b 00 0b 00 0a 00 fd ff 09 00 03 00
03 00 0a 00 02 00 03 00 02 00 08 00 07 00 fa ff
fd ff 05 00 05 00 05 00 01 00 f8 ff 00 00 0b 00
00 00 00 00 0a 00 f6 ff 03 00 06 00 f7 ff 0c 00
fd ff f1 ff 0d 00 f9 ff f6 ff 04 00 fd ff fb ff
0c 00 ff ff f7 ff 05 00 04 00 fe ff 00 00 fe ff
03 00 01 00 ff ff 07 00 fd ff fd ff 02 00 0b 00
fc ff 08 00 0f 00 fc ff ff ff 0c 00 00 00 03 00
0f 00 fc ff 05 00 05 00 ff ff ff ff 03 00 05 00
06 00 ff ff 04 00 06 00 f7 ff 0e 00 0e 00 f8 ff
01 00 06 00 f9 ff 06 00 0a 00 00 00 01 00 fd ff
01 00 08 00 fa ff ff ff fe ff 02 00 0b 00 fc ff
01 00 00 00 f6 ff 04 00 09 00 f8 ff fe ff 02 00
fe ff fa ff 07 00 02 00 fc ff fc ff 04 00 fd ff

波形で見るとRは00なので08-23-00-00-02-38-00-00のようなRは0埋めかと思ったけど、これでいいのか?これはI2Sで読み込んだLRのうち、Lだけをバッファに取り込んでいるということだろうか。だとすると、format=I2S.STEREO を指定するとRの2byte分は0で埋められる??
以下のようにSTEREO指定でI2Sを初期化、readnitoでバッファに読み込む

i2s = I2S(0, sck=Pin(18), ws=Pin(19), sd=Pin(20), mode=I2S.RX, bits=16, format=I2S.STEREO, rate=44100, ibuf=20000)
i2s.readinto(buf)

読み込んだデータをダンプさせると以下の通り、Rchは0埋めになっているので、I2S.MONO指定して実行するとL-chのみがバッファに入るようである。これはありがたい(モノラルマイクなので、本来片側Channelは不要なので)

>>> for i in range(0x100):
...   print(f'{buf[i]:02x} ',end='')
...   if (i+1) % 16 == 0:
...      print('')
...
ff 7f 00 00 06 00 00 00 0a 00 00 00 82 ff 00 00
84 ff 00 00 71 00 00 00 70 00 00 00 a0 00 00 00
1d 00 00 00 fc 00 00 00 7e ff 00 00 e4 01 00 00
29 fe 00 00 d9 03 00 00 6a fb 00 00 f9 07 00 00
20 f4 00 00 62 18 00 00 e1 fe 00 00 14 98 00 00
3d 78 00 00 84 63 00 00 b5 71 00 00 8f 68 00 00
b9 6e 00 00 80 6a 00 00 24 6d 00 00 69 6b 00 00
53 6c 00 00 c7 6b 00 00 f8 6b 00 00 d6 6b 00 00
bf 6b 00 00 b1 6b 00 00 a4 6b 00 00 96 6b 00 00
89 6b 00 00 7b 6b 00 00 6e 6b 00 00 60 6b 00 00
53 6b 00 00 45 6b 00 00 38 6b 00 00 2b 6b 00 00
1d 6b 00 00 10 6b 00 00 03 6b 00 00 f5 6a 00 00
e8 6a 00 00 db 6a 00 00 cd 6a 00 00 c0 6a 00 00
b3 6a 00 00 a5 6a 00 00 98 6a 00 00 8b 6a 00 00
7d 6a 00 00 70 6a 00 00 63 6a 00 00 55 6a 00 00
48 6a 00 00 3b 6a 00 00 2d 6a 00 00 20 6a 00 00

スマフォで440Hzを発生させて録音させたときのデータダンプは以下
16bit、44.1KHzサンプリング、MONO

I2S(0, sck=Pin(18), ws=Pin(19), sd=Pin(20), mode=I2S.RX, bits=16, format=I2S.MONO, rate=44100, ibuf=20000)
00 00 f4 ff 13 00 50 00 6a 00 d0 00 ab 00 63 01
b0 00 39 02 57 00 a4 03 24 ff a3 06 51 f9 98 24
e7 78 ed 5a 4e 6f 54 6a 7e 6d 49 6b aa 6c b0 6b
35 6c d5 6b f2 6b cd 6b c1 6b b3 6b a5 6b 98 6b
8a 6b 7d 6b 6f 6b 62 6b 54 6b 47 6b 39 6b 2c 6b
1e 6b 11 6b 04 6b f7 6a e9 6a dc 6a cf 6a c1 6a
b4 6a a7 6a 99 6a 8c 6a 7f 6a 71 6a 64 6a 57 6a
49 6a 3c 6a 2e 6a 21 6a 14 6a 07 6a f9 69 ec 69
df 69 d2 69 c5 69 b8 69 aa 69 9d 69 90 69 83 69
76 69 68 69 5b 69 4e 69 41 69 34 69 26 69 19 69
0c 69 ff 68 f2 68 e5 68 d8 68 cb 68 bd 68 b0 68
a3 68 96 68 89 68 7c 68 6f 68 62 68 55 68 48 68
3b 68 2e 68 21 68 14 68 07 68 f9 67 ec 67 e0 67
d3 67 c6 67 b9 67 ac 67 9f 67 92 67 85 67 78 67
6b 67 5e 67 51 67 44 67 38 67 2b 67 1e 67 11 67
04 67 f7 66 ea 66 dd 66 d0 66 c3 66 b7 66 aa 66

これを大体の波形にするなら、、44.1KHzサンプリングは440Hzの100倍なので、100サンプルあれば、440Hzの波形が一つとれているはず。1サンプル2Byteなので、200B取ってくればその中に一つの波形が見られるはず。2バイトの並びが先頭MSBだと仮定して、2バイトを16bit長の符号付き整数とみなして100サンプル分を取り出してみる。
といいつつ。符号化が面倒なので、まずは2Bを連結だけして100サンプル分をダンプ
上位バイト->下位バイトの並びと想定して2バイト化すると以下

>>> dump2B(buf)
0000 f4ff 1300 5000 6a00 d000 ab00 6301 b000 3902 5700 a403 24ff a306 51f9 9824
e778 ed5a 4e6f 546a 7e6d 496b aa6c b06b 356c d56b f26b cd6b c16b b36b a56b 986b
8a6b 7d6b 6f6b 626b 546b 476b 396b 2c6b 1e6b 116b 046b f76a e96a dc6a cf6a c16a
b46a a76a 996a 8c6a 7f6a 716a 646a 576a 496a 3c6a 2e6a 216a 146a 076a f969 ec69
df69 d269 c569 b869 aa69 9d69 9069 8369 7669 6869 5b69 4e69 4169 3469 2669 1969
0c69 ff68 f268 e568 d868 cb68 bd68 b068 a368 9668 8968 7c68 6f68 6268 5568 4868
3b68 2e68 2168 1468 

上位バイト→下位バイトの並びなのかどうかちょっと怪しい感じが・・
下位バイト→上位バイトの並びと仮定すると以下

>>> dump2Bl(buf)
0000 fff4 0013 0050 006a 00d0 00ab 0163 00b0 0239 0057 03a4 ff24 06a3 f951 2498
78e7 5aed 6f4e 6a54 6d7e 6b49 6caa 6bb0 6c35 6bd5 6bf2 6bcd 6bc1 6bb3 6ba5 6b98
6b8a 6b7d 6b6f 6b62 6b54 6b47 6b39 6b2c 6b1e 6b11 6b04 6af7 6ae9 6adc 6acf 6ac1
6ab4 6aa7 6a99 6a8c 6a7f 6a71 6a64 6a57 6a49 6a3c 6a2e 6a21 6a14 6a07 69f9 69ec
69df 69d2 69c5 69b8 69aa 699d 6990 6983 6976 6968 695b 694e 6941 6934 6926 6919
690c 68ff 68f2 68e5 68d8 68cb 68bd 68b0 68a3 6896 6889 687c 686f 6862 6855 6848
683b 682e 6821 6814 

こっちのほうが値に連続性が見られる。ただ、、波形は途中で切れているようなのだが。。しかも高止まりでしかないような。。
スマフォのスピーカをMICにびったりくっつけてみたら、以下のようなデータになった。2バイト目(奇数バイト)はやはり符号拡張された上位8bitであり、データの有効桁数は下位8bitで、1バイト目(偶数バイト)ではないかと思えるのだが。。やっぱりちゃんと仕様書を読まないとこれ以上分からない。

fc ff fb ff f5 ff f7 ff f6 ff fc ff fb ff f8 ff
f7 ff f5 ff f8 ff fe ff f8 ff fa ff fb ff fd ff
05 00 ff ff fd ff fc ff 03 00 05 00 fe ff fe ff
fb ff fd ff fe ff 01 00 fc ff f7 ff fa ff fa ff
02 00 fd ff f9 ff f7 ff fa ff 02 00 01 00 fb ff
f7 ff fb ff 05 00 07 00 01 00 ff ff fd ff 00 00
06 00 05 00 ff ff ff ff fe ff 07 00 0b 00 ff ff
03 00 fe ff 05 00 07 00 06 00 04 00 04 00 05 00
0a 00 0d 00 06 00 06 00 07 00 0d 00 08 00 05 00
05 00 03 00 09 00 0b 00 09 00 02 00 03 00 04 00
0b 00 0b 00 04 00 06 00 01 00 09 00 0b 00 07 00
07 00 07 00 09 00 09 00 0a 00 05 00 05 00 0a 00

どうも手探り状態では埒が明かないので、I2SバスのMEMS MICとI2Sスピーカを直結してみた。正確には、クロックだけはRP2040から出力して、それ以外のバスは直結。I2Sスピーカで聞いてみると、スマフォのスピーカをMEMS MICにピッタリくっつけるぐらいにしないと音を拾ってくれないようであった。前からずっと気になっていたのだが、MEMS MICってデジタルでいろいろお世話してあげないとめちゃ感度わるいのだろうか。。
音源のスマフォとMEMS MICがぴったりくっついて録音した時のデータは以下

>>> dump(buf,16*16)
39 0a 42 0a 60 0a 4f 0a 3f 0a 25 0a 04 0a b9 09
86 09 41 09 ec 08 a4 08 39 08 e5 07 76 07 0c 07
96 06 1e 06 71 05 e3 04 50 04 cc 03 2d 03 99 02
ff 01 6c 01 db 00 3a 00 97 ff dc fe 55 fe b5 fd
35 fd 96 fc 1a fc 9b fb 1f fb a2 fa 13 fa a6 f9
38 f9 cb f8 58 f8 f7 f7 9b f7 5c f7 f5 f6 b9 f6
7f f6 4a f6 23 f6 eb f5 eb f5 b3 f5 d8 f5 ae f5
e4 f5 f0 f5 1e f6 5e f6 81 f6 f1 f6 17 f7 99 f7
dd f7 50 f8 ba f8 3c f9 cd f9 59 fa fd fa 8d fb
3d fc d9 fc 84 fd 37 fe dd fe 85 ff 37 00 ea 00
a8 01 53 02 08 03 aa 03 45 04 e4 04 6b 05 fd 05
6f 06 e7 06 5a 07 cd 07 30 08 90 08 e6 08 3a 09
87 09 c2 09 f1 09 1d 0a 33 0a 4f 0a 4e 0a 43 0a
4a 0a 2d 0a 15 0a d9 09 a6 09 4a 09 11 09 ba 08
67 08 05 08 96 07 21 07 b7 06 3e 06 ae 05 22 05
75 04 df 03 55 03 d6 02 35 02 9d 01 fd 00 52 00

こうやってデータを見ていると、16bit幅の上位8ビットは2バイト目のように思える。そう解釈して符号付き16bit整数に変換してグラフ描画した結果は以下。44.1Hzに対して200サンプルとってグラフ化したので、440Hzは2波入っている、これだと計算通り

def dump2Bls(buf, len=128, onHex=False):
  for i in range(len):
     data = buf[i*2+1] << 8
     data += buf[i*2]
     if onHex:
         print(f'{data:02x} ',end='')
     if data >= 0x8000:
         print(data - 0x10000)
     else:
         print(data)

PDMのMEMS MICの時も感度の悪さを解消できなかったのだが、I2SバスのMEMS MICも同じ症状である。上位8bitしか扱えていないぐらいに感度悪い。ノイズ除去や増幅等の信号処理が必要なのかもしれない。GitにはMicroPythonのI2Sライブラリを使ってI2SバスのMEMSマイクで録音するサンプルもあるので、先人のコードも動かしてみるべきか・・
オーディオに使用される周波数について(サンプリング周波数、PCM,DSDなど) | マルツセレクト
離散フーリエ変換(DFT)の仕組みを完全に理解する #競技プログラミング - Qiita
うさぎでもわかる信号処理・制御工学 第12羽 離散時間フーリエ変換(DTFT) | 工業大学生ももやまのうさぎ塾

■追記
犬笛を買ったのでそれを吹いてMEMS MICで集音してみた。結果、12KHz相当の波形が取れた。これだとなんとか行けそうだ。

上記波形は、無音(低いボリュームの雑音)、笛を吹いた時の波形、机を叩いた時の波形を比べたもの。目で見てはっきり違いが分かる。笛を吹いているのかどうかはフーリエ級数の考え方を使えば検出できると期待。