PWMのduty比を変更するタイミングでpcmの値をバッファに入れて、
いい塩梅のタイミングでログファイルに書き込むようにして
ログファイルを作成
↓
PCでなんか色々確認する
再生されるはずの音源
adpcmファイルをwavに変換したもの
なめらかに再生されるね
出力を確認
出力ログを作った

pythonでwavに変換
ブツブツ鳴ってるね。
これでソフトとハードのどっちが原因か切り分けれた。
アンプとスピーカーは正常で、
adpcmの読み出しからPWMのduty比変更までの間にデータが欠損したりしてるっぽい??
DAWで波形見たら、0.13秒ごとにオフセット位置が急峻に切り替わってた。

原因
adpcmファイルが想定してる仕様じゃなかった。
ブロックごとのヘッダを取り除いて音声を表してる部分だけにしたつもりが、
動画ファイルからadpcmファイルを作るpythonスクリプトがミスっててブロックヘッダ込みのadpcmファイルが生成されてた。
PCでadpcmから変換したwavファイルが正常に再生されてたのは
ブロックヘッダが残ってたからこそ正常に再生できてしまっていたみたい…
修正
前回の記事からこのスクリプトの記述は消しておく。
"""
動画ファイルの音声を生の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:\\ffmpeg-master-latest-win64-gpl-shared\\bin\\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をバイナリで開く
with open(wav_path, "rb") as f:
data = f.read()
if data[0:4] != b"RIFF" or data[8:12] != b"WAVE":
raise ValueError("入力ファイルがWAVではありません")
# ---- fmtチャンクから block_align を取得 ----
fmt_pos = data.find(b"fmt ")
fmt_size = struct.unpack_from("<I", data, fmt_pos + 4)[0]
fmt_data = data[fmt_pos + 8 : fmt_pos + 8 + fmt_size]
if len(fmt_data) >= 12:
# fmt構造体: wFormatTag(2) + nChannels(2) + nSamplesPerSec(4) + nAvgBytesPerSec(4) + nBlockAlign(2)
block_align = struct.unpack_from("<H", fmt_data, 12)[0]
else:
block_align = 512 # fallback
print(f"検出された block_align = {block_align} bytes")
# ---- dataチャンクから実データ抽出 ----
idx = data.find(b"data")
if idx == -1:
raise ValueError("dataチャンクが見つかりません")
data_size = int.from_bytes(data[idx+4:idx+8], "little")
adpcm_bytes = data[idx+8:idx+8+data_size]
# ---- 各ブロック先頭4バイトを削除 ----
header_size = 4
cleaned_bytes = bytearray()
for i in range(0, len(adpcm_bytes), block_align):
block = adpcm_bytes[i : i + block_align]
if len(block) > header_size:
cleaned_bytes.extend(block[header_size:])
with open(out_path, "wb") as f:
f.write(cleaned_bytes)
os.remove(wav_path)
print(f"[Audio] {input_video} → {out_path} (block_align={block_align}, ヘッダ除去済)")改良

スピーカーの裏にマステを貼ってブレッドボードに貼り付けた。カンペキ。
結果
めっちゃスムーズ
高域のノイズがひどいからローパスフィルター組んだら何にも聴こえなくなってウケた。
画面上部の抵抗とコンデンサはその残骸。
おわりに
基本的な機能の実装がやっと終わった!!!!!!
次回ファイルをたくさん突っ込んで長時間運転してみる。
