リソースの管理ムズすぎ。
買ったもの
小さいスピーカーとアンプボード(DIP化済み)
(左の袋に入ってるオペアンプは今回使ってないよ。)

「電子工作ステーション」って通販サイトで購入したよ
はんだごて持ってないからはんだ付けして送ってくれる有料オプションマジで助かった
購入したときの記事↓
結線
繋ぐだけだからめっちゃ簡単
アンプボードのピン間が2.54mmピッチより狭くて
ブレッドボードに挿せなかったからジャンパーで無理矢理繋いだよ

片側のチャンネルだけなのにモジャモジャでウケる。
音声のフォーマット
元の動画ファイルから音声をひっぺがしてadpcm形式に変換する
adpcmの中身はwavヘッダ無しで、1バイト目から音声データ
16kHz、12bit、PWMで再生
音声の変換
動画ファイルをpythonで生のADPCMに変換
import os
import subprocess
output_dir = "任意の出力フォルダ名"
"""
動画ファイルの音声を生のIMA-ADPCM nibbleデータとして保存する
"""
def extract_audio_to_adpcm(input_video: str, output_dir: str):
basename = os.path.splitext(os.path.basename(input_video))[0]
wav_path = os.path.join(output_dir, basename + "_tmp.wav") # 一時WAV
out_path = os.path.join(output_dir, basename + ".adpcm") # 最終出力
# ffmpeg で一旦 IMA-ADPCM (WAV) に変換
# パス通してたらこんなん要らん
ffmpeg_path = "C:\\xxxx\\xxxx\\ffmpeg.exe"
cmd = [
ffmpeg_path,
"-y",
"-i", input_video,
"-ac", "1",
"-ar", "16000",
"-acodec", "adpcm_ima_wav",
wav_path
]
subprocess.run(cmd, check=True)
# WAVヘッダをスキップして ADPCM 部分を取り出す
with open(wav_path, "rb") as f:
data = f.read()
# fmt チャンクを探す
if data[0:4] != b"RIFF" or data[8:12] != b"WAVE":
raise ValueError("入力ファイルがWAVではありません")
# WAVフォーマットは"data"チャンクの後に実データが来る
idx = data.find(b"data")
if idx == -1:
raise ValueError("WAVにdataチャンクが見つかりません")
data_size = int.from_bytes(data[idx+4:idx+8], "little")
adpcm_bytes = data[idx+8:idx+8+data_size]
with open(out_path, "wb") as f:
f.write(adpcm_bytes)
# 一時WAVを削除
os.remove(wav_path)
print(f"[Audio] {input_video} → {out_path} (ヘッダ除去済)")役割分担
core0
・LCDへの描画
・adpcmをpcmにデコード ←New!!
・PWMで音声を再生 ←New!!
core1
・microSDからフレームデータの読み込み
・フレームデータの展開
・microSDからadpcmの読み込み ←New!!
再生までの流れ
core1で動画1フレーム分の描画データを読み出した直後に
音声も1フレーム分読み出してadpcmのバッファにプッシュ
↓
core0で動画を1フレーム描画した直後に
1フレーム分のadpcmをpcmにデコードしてpcmのバッファにプッシュ
この繰り返しで動画用のバッファも音声用のバッファも1フレーム分くらいは埋まってるようにしてある
PWMでの音声再生自体はタイマーを使って16kHz(62.5usに1回)で呼び出され続ける
イベントハンドラに着火
↓
pcmのバッファから1バイト(2サンプル)ポップして
duty比を変更
結果
ピッチ、再生速度、動画との同期は完璧にできた
でも1秒に6回くらいの頻度で音がブツブツ鳴っててまともに聴いてられない・・・
最後のクラッシュシンバルの減衰おかしいよね??
おわりに
タイマーの割り込みが悪さしてる とか メモリ足りない とか
色んな原因考えて色々(DMA転送、デコードするコアを変更)やってみたけど
まだ原因わかってない・・・
次回、これを解決して最終回にしようと思う

