将音频生成srt和fcpxml字幕文件
Whisper + FCPXML 自动字幕生成器解析
对音频文件进行语音识别并生成字幕文件 (SRT),(fcpxml)
对应剪映用户可略过GG
还是先上代码:
import whisper
import srt
import webrtcvad
import wave
import numpy as np
from datetime import timedelta
from pydub import AudioSegment
import os
# 加载 Whisper 模型
model = whisper.load_model("base")
def convert_mp3_to_wav(mp3_path):
""" 将 MP3 转换为 WAV (16kHz, 单声道, 16-bit PCM) """
wav_path = mp3_path.replace(".MP3", ".wav").replace(".mp3", ".wav")
audio = AudioSegment.from_file(mp3_path, format="mp3")
audio = audio.set_channels(1).set_frame_rate(16000).set_sample_width(2) # 16-bit PCM
audio.export(wav_path, format="wav")
return wav_path
def is_voice(frame, vad):
""" 判断音频帧是否包含人声 """
return vad.is_speech(frame, sample_rate=16000)
def vad_filter(audio_path):
""" 处理音频并检测有人声的片段 """
vad = webrtcvad.Vad(2) # 0 = 最宽松, 3 = 最严格
with wave.open(audio_path, "rb") as wf:
rate = wf.getframerate()
assert rate == 16000, f"音频采样率错误: {rate}Hz (必须是 16000Hz)"
width = wf.getsampwidth()
assert width == 2, f"音频格式错误: 需要 16-bit PCM, 但读取到 {width * 8}-bit"
channels = wf.getnchannels()
assert channels == 1, f"音频通道错误: 需要单声道, 但读取到 {channels} 声道"
frames = wf.readframes(wf.getnframes())
# 转换音频数据
audio_array = np.frombuffer(frames, dtype=np.int16)
# 设置帧长(必须是 10ms, 20ms, 30ms)
frame_duration = 30 # 30ms
frame_size = int(rate * frame_duration / 1000) # 30ms 的采样点数量
frame_bytes = frame_size * 2 # 每个采样点 2 字节 (16-bit PCM)
non_silent_ranges = []
# 遍历音频帧
for i in range(0, len(audio_array) * 2, frame_bytes): # 以字节为单位切割
frame = frames[i:i + frame_bytes]
# 忽略过短的帧
if len(frame) < frame_bytes:
break
if is_voice(frame, vad):
start_time = (i / 2) / rate # 计算开始时间
end_time = ((i + frame_bytes) / 2) / rate # 计算结束时间
non_silent_ranges.append((start_time, end_time))
return non_silent_ranges
def generate_fcpxml(subtitles, fcpxml_path, media_path):
""" 生成 FCPXML 文件 """
fcpxml_template = f'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fcpxml>
<fcpxml version="1.9">
<resources>
<format id="r1" frameDuration="1/24s" />
<asset id="r2" name="{os.path.basename(media_path)}" src="file://{media_path}" />
</resources>
<library>
<event name="自动字幕">
<project name="{os.path.basename(media_path)}">
<sequence format="r1" duration="{subtitles[-1].end.total_seconds()}s">
<spine>
'''
for sub in subtitles:
start = sub.start.total_seconds()
duration = (sub.end - sub.start).total_seconds()
fcpxml_template += f'''
<caption start="{start}s" duration="{duration}s" format="itt" lane="1" offset="{start}s" role="subtitle">{sub.content}</caption>
'''
fcpxml_template += '''
</spine>
</sequence>
</project>
</event>
</library>
</fcpxml>
'''
with open(fcpxml_path, "w", encoding="utf-8") as f:
f.write(fcpxml_template)
print(f"FCPXML 生成完成: {fcpxml_path}")
# 你的 transcribe_audio 函数中,调用 generate_fcpxml
def transcribe_audio(file_path):
""" 处理音频,转换字幕并过滤无声部分 """
# 转换 MP3 到 WAV
if file_path.lower().endswith(".mp3"):
file_path = convert_mp3_to_wav(file_path)
non_silent_ranges = vad_filter(file_path)
# 运行 Whisper 识别
result = model.transcribe(file_path, language="zh", task="transcribe")
subtitles = []
for segment in result["segments"]:
start, end, text = segment["start"], segment["end"], segment["text"]
# 只保留有人声的字幕
if any(s <= start <= e for s, e in non_silent_ranges):
subtitles.append(srt.Subtitle(index=len(subtitles) + 1,
start=timedelta(seconds=start),
end=timedelta(seconds=end),
content=text))
# 生成 SRT 文件
srt_path = file_path.replace(".wav", ".srt")
with open(srt_path, "w", encoding="utf-8") as f:
f.write(srt.compose(subtitles))
print(f"字幕 SRT 生成完成: {srt_path}")
# 生成 FCPXML 文件
fcpxml_path = file_path.replace(".wav", ".fcpxml")
generate_fcpxml(subtitles, fcpxml_path, file_path)
# 运行
transcribe_audio("audio.MP3") # 替换为你的音频文件名
代码的主要功能是:
使用 OpenAI Whisper 识别音频(支持 MP3、WAV)。
通过 WebRTC VAD 过滤无声片段,确保只提取有人声的部分。
生成 SRT 字幕文件(兼容大部分视频编辑软件)。
生成 Final Cut Pro X (FCPX) 可用的 FCPXML 字幕文件,方便直接导入到视频编辑软件中。
采用 60fps (frameDuration="1/60s") 适配高帧率视频。可自行修改为24fps,或你想要的帧率
如果音频是 MP3,先转换为 16kHz, 单声道, 16-bit PCM 格式的 WAV。
修改最后一行的
transcribe_audio("audio.MP3")
输入音频文件路径,自动执行 MP3 转换、语音识别、字幕生成。
这段代码可以用于 自动字幕生成,特别适合 视频博主、剪辑师 直接导入 Final Cut Pro X,省去手动调整字幕的麻烦。🚀
如果你有更多优化需求,比如 字体样式、字幕位置,可以进一步修改 generate_fcpxml() 逻辑。💡
版权声明:
作者:José
链接:https://www.josejang.com/about-work/320.html
来源:José's ⎝⏠⏝⏠⎠ Blog
文章版权归作者所有,未经允许请勿转载。
THE END
二维码
共有 0 条评论