INMP441マイクを載せたESP32-S3で音声を取り込み、USB-CDC経由でAndroidに16kHz・16bit PCMをストリーミング。
スマホ側ではJetpack ComposeのCanvasで、0〜800Hzのスペクトログラムをリアルタイム描画します。
将来的には、お手本の歌声と自分の声のスペクトログラムを並べ、「どの周波数帯が足りないか・出し過ぎか」を視覚的にフィードバックするボーカルトレーニングツールを目指しています。:contentReference[oaicite:0]{index=0}
ただ録音して聞き返すだけでは、「何が違うのか」がわかりにくい。 そこで、声の周波数成分をリアルタイムに「絵」として見せて、 目で見ながら音域や発声を調整できる仕組みを作りました。
ESP32-S3側ではI2SでINMP441マイクから24bit左詰めの音声を取得し、 DCカット用の一次IIRハイパスフィルタをかけてから16bit PCMに変換。 このPCMをUSB-CDCシリアル経由でAndroidスマホに送り、 スマホ側でSTFT(短時間フーリエ変換)+dBスケールに変換して、 Canvas上に時系列スペクトログラムとして表示します。
左側にINMP441マイク搭載のESP32-S3ボード、右側にAndroidスマホ。 ESP32はI2Sでマイクから音声を取得し、16kHz・16bitモノラルのPCMとしてUSB-CDC経由で送信します。 AndroidアプリはUSBデバイスとしてESP32を認識し、取得したPCMをそのままSTFTに流し込んでスペクトログラムを描画します。:contentReference[oaicite:1]{index=1}
フリーノーブESP32-S3 Liteボード上で、INMP441マイクをI2S接続しています。
INMP441は24bit左詰めで出力するため、ESP32側では32bit枠でサンプルを受信し、
右シフト(14ビット)によって16bitにスケーリング。
同時に、y[n] = a ( y[n-1] + x[n] - x[n-1] ) という一次IIRハイパスを実装し、
20Hz付近を目安にDC成分と超低域をカットすることで、メーターのふらつきを抑えています。
Android側ではUSB Host APIでCDCデバイスを検出し、
Bulk INエンドポイントからバイト列を取得します。取得したデータをByteBufferでShortArrayに変換し、
STFT用フレームバッファに順次格納。
フレーム長は256サンプル、ホップは64サンプル(75%オーバーラップ)でHann窓をかけた後、
自前実装のRadix-2 FFTで周波数領域に変換しています。:contentReference[oaicite:2]{index=2}
スペクトログラムは、縦軸=時間、横軸=周波数という形でCanvasに描画しています。 新しいフレームが来るたびに、既存の行を1つずつ上にシフトし、 一番下の行に最新フレームを書き込むことで、 常に「画面下がいま鳴っている音」になるようにしました。
参照用の「お手本音声」は、MediaExtractorで音声トラックを選択し、MediaCodecでPCM16にデコードしています。
チャンネルが複数ある場合は単純平均してモノラル化し、
その後16kHzに線形補間でリサンプリング。
リアルタイム処理と同じ条件でSTFTを行い、refSpecとして2次元配列に格納します。
refSpec - userSpec で差分カラー表示に発展予定。
ホワイトノイズ、正弦波(100Hz, 300Hz, 600Hz)などをESP32側から入力し、 Android上のスペクトログラムに期待通りの帯が表示されるかを確認しました。
現状では、リアルタイムのスペクトログラムとレベルメータの表示は安定しており、
「音がどれくらい出ているか」を視覚的に確認できます。
一方で、「お手本スペクトログラムとの差分」を色で重ねるUIはまだ実装途中であり、
比較は頭の中で行う必要があります。
ESP32のマイクに向かって声を出し、そのスペクトログラムがスマホ上でリアルタイムに立ち上がる様子を撮影したデモ動画です。
目標は、好きな歌手やVtuberの音声を読み込んで、 「今の自分の声は、どの周波数帯が足りないか/出し過ぎか」をリアルタイムで色分けして教えてくれるアプリにすることです。
ハード・DSP・UI・学習体験の4つの軸で改善していきます。
マイコンとAndroidをつないでリアルタイム処理をするのは、
想像以上に「地味な不具合」との戦いでした。
USBのエンドポイントが見つからなかったり、I2Sのビットシフトを1つ間違えただけで、
スペクトログラムが真っ黒になってしまう。
その一方で、初めて自分の声がスペクトログラムとしてスマホに流れた瞬間は、
単純に「うわ、楽しい」と思いました。