AIで作る音声素材を配る前に、44.1kHzの1ファイルを48kHzパックから止める

AIで作る音声素材を配る前に、44.1kHzの1ファイルを48kHzパックから止める

48kHzでそろえたつもりの音声素材パックへ、44.1kHzのvoice-44100.wavを1ファイルだけ混ぜた見本を作りました。フォルダを開くとどれもWAVです。しかし、音声編集ソフトへ並べた後で変換が走ると、素材ごとの時間や書き出し条件を確かめる手間が増えます。

AIに効果音の案やファイル名を頼むと、素材の数はすぐ増えます。波形を1本ずつ再生しても、44.1kHzと48kHzの混在は見分けられません。そこで販売物そのものではなく、3ファイルの合成パックで形式だけを止める小さな検査を試しました。

ここでのWAVは、音のデータにサンプルレート、チャンネル数、サンプル幅を持つファイルです。サンプルレートは1秒間に記録する回数、チャンネル数はモノラルなら1、ステレオなら2を表します。今回の契約は、16bit・48kHz・モノラルです。

先に「そろっている」の定義を置く

WAVを開けることと、素材パックとしてそろっていることは別です。44.1kHzは音楽配信でよく使われ、48kHzは動画編集でよく使われます。どちらかが誤りなのではありません。購入者へ渡す1つのパックで、どちらを約束するかを先に決める必要があります。

Python標準ライブラリのwaveは、WAVのチャンネル数、サンプル幅、サンプルレートを読み書きできます。この見本では16bit PCMの短い音を作り、各値を読むだけに絞りました。音量、ノイズ、音楽としての良し悪しは判定しません。

拡張子が.wavなら、検査対象として十分に見えます。ただ、同じ拡張子でも、編集時にそろえたい値は残ります。形式の契約を小さく書いておけば、音の評価と混ぜずに機械で止められます。

合成パックを3つ作る

releaseは合格用です。mixedには44.1kHzのファイルとステレオのファイルを混ぜます。emptyは、WAVが1本もないフォルダです。

次のコードを$TMPDIR/wav-format-lab/make_samples.pyとして保存しました。$TMPDIRは、その場で消してよい一時ディレクトリです。販売用の音声や購入者のデータは使っていません。

make_samples.py
import shutil
import wave
from pathlib import Path

root = Path(__file__).parent
for name in ("release", "mixed", "empty"):
    folder = root / name
    if folder.exists():
        shutil.rmtree(folder)
    folder.mkdir()


def write(path: Path, rate: int, channels: int) -> None:
    with wave.open(str(path), "wb") as out:
        frames = rate // 100
        out.setparams((channels, 2, rate, frames, "NONE", "not compressed"))
        out.writeframes(b"\0\0" * channels * frames)


for folder, name, rate, channels in (
    ("release", "click.wav", 48_000, 1),
    ("release", "tap.wav", 48_000, 1),
    ("mixed", "click.wav", 48_000, 1),
    ("mixed", "voice-44100.wav", 44_100, 1),
    ("mixed", "stereo.wav", 48_000, 2),
):
    write(root / folder / name, rate, channels)
print("created release=2 mixed=3 empty=0")

3つの値だけを読み、違えば失敗にする

検査側では、*.wavが0本なら失敗にします。見つけた各ファイルからサンプルレート、チャンネル数、サンプル幅を読みます。指定した16bit・48kHz・モノラルと1つでも違えば、ファイル名と値を出して終了コード1にします。

check_wav_format.py
import sys
import wave
from pathlib import Path

EXPECTED = {"channels": 1, "sample_width": 2, "sample_rate": 48_000}


def main(folder: Path) -> int:
    files = sorted(folder.glob("*.wav"))
    if not files:
        print(f"NG {folder.name}: .wav がありません")
        return 1
    errors = 0
    for path in files:
        with wave.open(str(path), "rb") as source:
            actual = {
                "channels": source.getnchannels(),
                "sample_width": source.getsampwidth(),
                "sample_rate": source.getframerate(),
            }
        for label, expected in EXPECTED.items():
            if actual[label] != expected:
                unit = "Hz" if label == "sample_rate" else ""
                print(f"NG {path.name}: {label}={actual[label]}{unit} expected={expected}{unit}")
                errors += 1
    if errors:
        print(f"NG files={len(files)} errors={errors}")
        return 1
    print("OK files=%d sample_rate=48000Hz channels=1 sample_width=2byte" % len(files))
    return 0


raise SystemExit(main(Path(sys.argv[1])))

このコードを$TMPDIR/wav-format-lab/check_wav_format.pyとして保存し、見本を作ってから順に実行しました。

cd "$TMPDIR/wav-format-lab"
python3 --version
python3 make_samples.py
python3 check_wav_format.py release; printf 'release_exit=%s\n' "$?"
python3 check_wav_format.py mixed; printf 'mixed_exit=%s\n' "$?"
python3 check_wav_format.py empty; printf 'empty_exit=%s\n' "$?"
Python 3.13.5
created release=2 mixed=3 empty=0
OK files=2 sample_rate=48000Hz channels=1 sample_width=2byte
release_exit=0
NG stereo.wav: channels=2 expected=1
NG voice-44100.wav: sample_rate=44100Hz expected=48000Hz
NG files=3 errors=2
mixed_exit=1
NG empty: .wav がありません
empty_exit=1

見たいのは、形式だけ。音色の良し悪しは、別の確認へ回します。先に止める条件は何か。購入者が聞く前に直せる部分を、ここで切り分けます。

mixedは2件を出して停止しました。サンプルレートとチャンネル数を別々に表示するので、変換が必要なファイルを探し直さずに済みます。空のフォルダも失敗にしたため、素材を入れ忘れたZIPを合格にしません。

変換まで自動化しない理由

この検査は、見つけたファイルを勝手に48kHzへ変換しません。変換すると、元の音声を残すのか、出力先をどこにするのか、ステレオをモノラルへ寄せるのかという別の判断が始まるからです。

まず混在を知らせ、素材の用途を見て人が直します。効果音ならモノラル化を選ぶ場面があります。BGMではステレオを保つほうが自然な場合もあります。形式の確認と音の編集を分けておくと、検査が意図しない音の変更を起こしません。

検査が見るのは、パックへ入れる直前の値です。元ファイルと変換後のファイルを同じフォルダへ置くなら、販売対象だけを別のフォルダへ分けてから実行します。

合格後に残る確認

この検査を通っても、音声素材の説明まで完成したわけではありません。各ファイルの長さ、音量、利用できる場面、ループの有無は別に確認します。音の内容を数値3つだけで保証しないためです。

販売ページには、48kHz・モノラル・16bitのように、購入者が再生環境を選ぶ前に役立つ値を記します。用途を限定するなら、動画編集向けなのか、ゲーム向けなのかも書きます。検査と説明を同じ条件でそろえると、パックを更新したときに見直す場所が明確になります。

販売前に契約を変えるなら、検査も一緒に変える

16bit・48kHz・モノラルが、すべての音声素材に最適な値ではありません。BGMならステレオを選ぶことがあります。音楽制作向けなら44.1kHzにそろえる判断もあります。

大切なのは、商品説明の値と検査で置いた期待値を同じ決定から置くことです。説明だけを48kHzへ直して検査を44.1kHzのままにすると、購入者へ渡す条件と作業側の条件が分かれます。

AIの出力を増やす工程では、生成の速さより検査の境界が効きます。まず1パックに必要な形式を1行で決め、ZIPを作る直前にこの検査を通します。それなら音の中身を評価する前に、渡し方のばらつきを減らせます。

出典

この記事をシェア