世の中的に需要はないかと思いますが、 すぐ忘れる未来の自分用のメモです...
さるところからRealMedia(え?今どき?)で構築された音声データベース(何じゃそりゃ?)からデータを取り出してほしい、すぐ欲しい、という依頼があり、その時は件数も少なかったので
- htmlのソースからsmilファイルの場所を探す
- smilファイルをエディターで開いて
- .rmファイルの場所、start/end timeを特定して
- VLCでwavに変換し
- さすがにここはバッチ処理しました。
- でもいらなかった。
- rmはAudacityで直接ひらけました...
- Audacityでstart/end timeでトリムしてwavでエクスポート(ここは手動...)
というようなことを延々としました。
これって自動化できるよね?ということで、smilファイルと同階層に置いて、同名のwavを作って、outputというフォルダに入れてくれるスクリプトを考えてみました。
import xml.etree.ElementTree as ET
import subprocess
from datetime import timedelta
# smilから rmのパスの文字列、 開始(秒:float)、 終了(秒:float)のタプル に
rm_info = lambda smil_file : (
(audio_attrib :=
ET.parse(smil_file)
.getroot()
.find("body")
.find("par")
.find("audio")
.get
)("src")
.replace("\\","/") # セパレーターのバックスラッシュをスラッシュに変換
, (seconds_from_attrib := lambda attrib_str :
time_in_seconds(
audio_attrib(attrib_str)[4:] # 文字列先頭の"nts="を除去
)
)("clip-begin")
, seconds_from_attrib("clip-end")
)
# hh:mm:ss.ss/mm:ss.ss/ss.ss形式から 秒:float に
time_in_seconds = lambda time_str : timedelta(
0 # days
, (times_split_reversed := [
float(x)
for x
in time_str
.split(":")
[::-1]
]
)[0] # seconds
, 0 # milliseconds
, 0 # microseconds
, *times_split_reversed[1:] # minutes, hours (optional)
).total_seconds()
def convert_to_wav(rm_file, start_time, end_time, output_file):
subprocess.run(
[
"/opt/homebrew/bin/ffmpeg",
"-ss",
str(start_time),
"-t",
str(end_time - start_time),
"-i",
rm_file,
"-acodec",
"pcm_s16le",
"-ar",
"44100",
output_file,
]
)
if __name__ == "__main__":
import glob
import os
out_path = "../output/"
os.makedirs(out_path, exist_ok=True)
for smil_file in glob.glob("*.smi") :
convert_to_wav(
*rm_info(smil_file)
, f"{out_path}{os.path.splitext( os.path.basename( smil_file))[0]}.wav"
)
ffmpegの位置は自分の環境での決め打ちになってます。
エラー処理はしてません。
とりあえず目的は果せたのでここに残しておきます。