Raspberry Pi PicoでAT-Xを作る⑰ 音声がブツブツ鳴るのを解決する

raspberry pi pico
oplus_0

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}, ヘッダ除去済)")

改良

スピーカーの裏にマステを貼ってブレッドボードに貼り付けた。カンペキ。

結果

めっちゃスムーズ

高域のノイズがひどいからローパスフィルター組んだら何にも聴こえなくなってウケた。
画面上部の抵抗とコンデンサはその残骸。

おわりに

基本的な機能の実装がやっと終わった!!!!!!

次回ファイルをたくさん突っ込んで長時間運転してみる。

タイトルとURLをコピーしました