SenseVoice-small-onnx语音识别教程:结果时间戳对齐与可视化展示

1. 引言

语音识别技术正在快速融入我们的日常工作流。无论是会议纪要、访谈整理,还是视频字幕生成,将音频内容快速、准确地转化为带时间戳的文本,已经成为一项高频需求。

然而,很多朋友在实际使用语音识别服务时,会遇到一个共同的困扰:识别出来的文字虽然准确,但不知道每句话、每个词在音频中的具体位置。这就好比拿到了一份会议记录,却不知道每段发言对应的是哪个时间点,想要回听核对或者制作字幕时,就变得非常麻烦。

今天,我要分享的就是如何利用 SenseVoice-small-onnx 这个轻量高效的语音识别模型,不仅实现多语言转写,还能精确获取每个识别结果的时间戳信息,并通过可视化方式直观展示。无论你是开发者、内容创作者,还是普通用户,都能通过这篇教程,轻松掌握这项实用技能。

2. 环境准备与快速部署

2.1 系统要求与依赖安装

首先,确保你的系统环境满足以下基本要求:

  • Python 3.8 或更高版本
  • 至少 2GB 可用内存(用于模型加载和推理)
  • 基本的命令行操作能力

接下来,我们安装必要的依赖包。打开终端,执行以下命令:

# 安装核心依赖
pip install funasr-onnx gradio fastapi uvicorn soundfile jieba

# 可选:安装可视化相关库
pip install matplotlib pandas

这里简单解释一下每个包的作用:

  • funasr-onnx:SenseVoice模型的ONNX推理框架
  • gradio:用于快速构建Web界面
  • fastapiuvicorn:用于创建REST API服务
  • soundfile:音频文件读取库
  • jieba:中文分词工具(用于更精确的时间戳对齐)
  • matplotlibpandas:数据可视化和处理

2.2 一键启动语音识别服务

SenseVoice-small-onnx已经提供了完整的服务部署脚本。下载项目文件后,只需要一条命令就能启动服务:

# 进入项目目录
cd sensevoice-small-onnx-quant

# 启动服务(默认端口7860)
python3 app.py --host 0.0.0.0 --port 7860

启动成功后,你会在终端看到类似下面的输出:

INFO:     Started server process [12345]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:7860 (Press CTRL+C to quit)

现在,打开浏览器访问 http://localhost:7860,就能看到语音识别的Web界面了。

3. 基础概念:什么是时间戳对齐?

在深入实践之前,我们先花几分钟理解一下"时间戳对齐"这个概念。这能帮你更好地理解后续的操作和结果。

3.1 时间戳对齐的核心价值

想象一下这样的场景:你有一段30分钟的会议录音,语音识别服务把它转成了文字。如果没有时间戳,你看到的只是一大段文字。但如果有时间戳对齐,你会看到:

[00:01:23 - 00:01:45] 张三:我们本季度的销售额同比增长了15%
[00:01:46 - 00:02:10] 李四:这个增长主要来自华东地区的新客户

时间戳对齐就是给识别出的每个词、每句话打上"时间标签",告诉你它在音频中的开始时间和结束时间。这带来了几个实实在在的好处:

  1. 精准定位:快速跳转到音频的任意位置
  2. 字幕生成:自动生成带时间轴的字幕文件
  3. 内容检索:根据关键词查找对应的音频片段
  4. 说话人分析:统计每个人的发言时长和时段

3.2 SenseVoice的时间戳输出格式

SenseVoice-small-onnx模型在识别时,除了返回转写文本,还会返回详细的时间戳信息。这些信息通常以两种格式提供:

格式一:词级别时间戳

{
  "text": "今天天气真好",
  "words": [
    {"word": "今天", "start": 0.0, "end": 0.5},
    {"word": "天气", "start": 0.5, "end": 1.0},
    {"word": "真好", "start": 1.0, "end": 1.5}
  ]
}

格式二:句子级别时间戳

{
  "text": "今天天气真好。我们出去走走吧。",
  "sentences": [
    {"text": "今天天气真好", "start": 0.0, "end": 1.5},
    {"text": "我们出去走走吧", "start": 2.0, "end": 3.5}
  ]
}

在实际应用中,我们可以根据需求选择不同的粒度。制作字幕通常用句子级别,而语音分析可能更需要词级别信息。

4. 获取带时间戳的识别结果

现在,我们进入实战环节。我将展示三种获取时间戳信息的方法,从简单到复杂,满足不同场景的需求。

4.1 方法一:使用Web界面(最简单)

对于不熟悉编程的朋友,Web界面是最友好的选择。

  1. 打开浏览器,访问 http://localhost:7860
  2. 点击"上传音频"按钮,选择你的音频文件
  3. 在语言选择中,可以选"auto"(自动检测)或指定语言
  4. 勾选"输出时间戳"选项
  5. 点击"转写"按钮

等待几秒钟,你会在右侧看到识别结果。如果勾选了时间戳选项,结果会以带时间标记的格式显示:

[00:00:00.000 - 00:00:01.230] 你好,欢迎使用语音识别服务。
[00:00:01.231 - 00:00:03.450] 这是一个多语言识别演示。

小技巧:对于中文音频,建议同时勾选"使用ITN"(逆文本正则化),这样数字、百分比等会被自动转换,比如"百分之二十"会变成"20%"。

4.2 方法二:调用REST API(适合开发者)

如果你需要在其他程序中集成语音识别功能,API调用是最佳选择。

基础调用示例

curl -X POST "http://localhost:7860/api/transcribe" \
  -F "file=@meeting.wav" \
  -F "language=zh" \
  -F "output_timestamps=true" \
  -F "use_itn=true"

Python代码调用示例

import requests
import json

# API端点
url = "http://localhost:7860/api/transcribe"

# 准备请求数据
files = {'file': open('audio.wav', 'rb')}
data = {
    'language': 'auto',
    'output_timestamps': 'true',
    'timestamp_level': 'word'  # 可选:word(词级)或sentence(句级)
}

# 发送请求
response = requests.post(url, files=files, data=data)
result = response.json()

# 解析结果
if result['success']:
    text = result['text']
    timestamps = result['timestamps']  # 时间戳信息
    print(f"识别文本:{text}")
    print(f"时间戳:{json.dumps(timestamps, indent=2, ensure_ascii=False)}")
else:
    print(f"识别失败:{result['error']}")

API返回的完整数据结构

{
  "success": true,
  "text": "完整的识别文本",
  "timestamps": [
    {
      "text": "第一句话",
      "start": 0.0,
      "end": 1.5,
      "words": [
        {"word": "第一", "start": 0.0, "end": 0.5},
        {"word": "句话", "start": 0.5, "end": 1.5}
      ]
    }
  ],
  "language": "zh",
  "duration": 30.5
}

4.3 方法三:直接使用Python SDK(最灵活)

对于需要深度定制或批量处理的场景,直接使用Python SDK提供了最大的灵活性。

from funasr_onnx import SenseVoiceSmall
import json

# 初始化模型(使用缓存路径)
model_path = "/root/ai-models/danieldong/sensevoice-small-onnx-quant"
model = SenseVoiceSmall(model_path, batch_size=1, quantize=True)

# 识别音频并获取时间戳
audio_files = ["speech.wav"]
results = model(audio_files, 
                language="auto",
                use_itn=True,
                return_timestamps=True,  # 关键参数:返回时间戳
                timestamp_level="word")  # 词级别时间戳

# 处理结果
for i, result in enumerate(results):
    print(f"音频 {i+1} 识别结果:")
    print(f"文本:{result['text']}")
    
    # 提取时间戳信息
    if 'timestamps' in result:
        print("\n时间戳详情:")
        for ts in result['timestamps']:
            start_time = ts['start']
            end_time = ts['end']
            word = ts['word']
            print(f"  [{start_time:.3f}s - {end_time:.3f}s] {word}")
    
    # 也可以获取句子级别的时间戳
    sentences = result.get('sentences', [])
    if sentences:
        print("\n句子级别时间戳:")
        for sent in sentences:
            print(f"  [{sent['start']:.3f}s - {sent['end']:.3f}s] {sent['text']}")

参数说明

  • return_timestamps=True:告诉模型返回时间戳信息
  • timestamp_level="word":指定时间戳粒度,可选"word"或"sentence"
  • batch_size=1:批处理大小,对于带时间戳的任务,建议设为1以获得更准确的时间信息

5. 时间戳数据的可视化展示

获取到时间戳数据后,我们可以用各种方式将其可视化,让结果更加直观易懂。

5.1 生成时间轴字幕文件

字幕文件是最实用的输出格式之一,可以直接用于视频编辑或播放器显示。

生成SRT字幕文件

def generate_srt(timestamps, output_file="output.srt"):
    """生成SRT格式字幕文件"""
    with open(output_file, 'w', encoding='utf-8') as f:
        for i, sentence in enumerate(timestamps, 1):
            # 转换时间格式(秒 -> 时:分:秒,毫秒)
            start_time = format_time(sentence['start'])
            end_time = format_time(sentence['end'])
            
            # 写入SRT格式
            f.write(f"{i}\n")
            f.write(f"{start_time} --> {end_time}\n")
            f.write(f"{sentence['text']}\n\n")

def format_time(seconds):
    """将秒数转换为SRT时间格式"""
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    secs = seconds % 60
    return f"{hours:02d}:{minutes:02d}:{secs:06.3f}".replace('.', ',')

# 使用示例
sentences = [
    {"text": "欢迎来到今天的会议", "start": 0.0, "end": 2.5},
    {"text": "我们首先回顾上季度业绩", "start": 2.5, "end": 5.0},
]
generate_srt(sentences, "meeting_subtitles.srt")

生成的SRT文件内容:

1
00:00:00,000 --> 00:00:02,500
欢迎来到今天的会议

2
00:00:02,500 --> 00:00:05,000
我们首先回顾上季度业绩

生成VTT字幕文件(Web字幕格式):

def generate_vtt(timestamps, output_file="output.vtt"):
    """生成WebVTT格式字幕文件"""
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write("WEBVTT\n\n")
        
        for i, sentence in enumerate(timestamps, 1):
            start_time = format_time_vtt(sentence['start'])
            end_time = format_time_vtt(sentence['end'])
            
            f.write(f"{start_time} --> {end_time}\n")
            f.write(f"{sentence['text']}\n\n")

def format_time_vtt(seconds):
    """将秒数转换为VTT时间格式"""
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    secs = seconds % 60
    return f"{hours:02d}:{minutes:02d}:{secs:06.3f}"

# 使用方式相同
generate_vtt(sentences, "meeting_subtitles.vtt")

5.2 创建交互式时间轴图表

对于数据分析或演示场景,图表能更直观地展示语音内容的时间分布。

import matplotlib.pyplot as plt
import matplotlib.patches as patches

def plot_speech_timeline(timestamps, title="语音时间轴"):
    """绘制语音时间轴图表"""
    fig, ax = plt.subplots(figsize=(12, 6))
    
    # 设置颜色映射(不同句子用不同颜色)
    colors = plt.cm.Set3(range(len(timestamps)))
    
    # 绘制每个句子的时间块
    for i, sentence in enumerate(timestamps):
        start = sentence['start']
        duration = sentence['end'] - sentence['start']
        text = sentence['text'][:20] + "..." if len(sentence['text']) > 20 else sentence['text']
        
        # 创建矩形块
        rect = patches.Rectangle(
            (start, i*0.8), 
            duration, 0.6,
            linewidth=1, 
            edgecolor='black',
            facecolor=colors[i],
            alpha=0.7
        )
        ax.add_patch(rect)
        
        # 添加文本标签
        ax.text(start + duration/2, i*0.8 + 0.3, 
                text, ha='center', va='center', fontsize=9)
    
    # 设置图表属性
    ax.set_xlabel('时间 (秒)', fontsize=12)
    ax.set_ylabel('句子序号', fontsize=12)
    ax.set_title(title, fontsize=14)
    ax.set_xlim(0, max(ts['end'] for ts in timestamps) + 5)
    ax.set_ylim(-0.5, len(timestamps)*0.8)
    ax.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.savefig('speech_timeline.png', dpi=150, bbox_inches='tight')
    plt.show()

# 使用示例
timestamps = [
    {"text": "开场介绍和欢迎词", "start": 0, "end": 45},
    {"text": "第一季度业绩汇报", "start": 45, "end": 180},
    {"text": "市场趋势分析", "start": 180, "end": 300},
    {"text": "问答环节", "start": 300, "end": 420},
]
plot_speech_timeline(timestamps, "会议发言时间分布")

5.3 构建Web可视化界面

结合Gradio,我们可以快速创建一个交互式的可视化工具。

import gradio as gr
import plotly.graph_objects as go
from datetime import datetime

def visualize_timestamps(audio_file, language="auto"):
    """处理音频并生成可视化结果"""
    # 调用识别API(这里简化,实际需要调用模型)
    # result = model([audio_file], language=language, return_timestamps=True)
    
    # 模拟数据(实际使用时替换为真实识别结果)
    result = {
        "text": "这是一个测试音频,包含多句话语。时间戳对齐功能非常实用。",
        "timestamps": [
            {"text": "这是一个测试音频,", "start": 0.0, "end": 2.5},
            {"text": "包含多句话语。", "start": 2.5, "end": 4.0},
            {"text": "时间戳对齐功能非常实用。", "start": 4.0, "end": 7.0}
        ]
    }
    
    # 创建时间轴图表
    fig = go.Figure()
    
    for i, ts in enumerate(result['timestamps']):
        fig.add_trace(go.Scatter(
            x=[ts['start'], ts['end']],
            y=[i, i],
            mode='lines+markers',
            line=dict(width=10),
            marker=dict(size=15),
            name=ts['text'][:20] + "...",
            hoverinfo='text',
            hovertext=f"文本: {ts['text']}<br>时间: {ts['start']:.2f}s - {ts['end']:.2f}s"
        ))
    
    fig.update_layout(
        title="语音识别时间轴",
        xaxis_title="时间 (秒)",
        yaxis_title="句子序号",
        showlegend=True,
        height=400
    )
    
    # 生成SRT字幕
    srt_content = "WEBVTT\n\n"
    for i, ts in enumerate(result['timestamps'], 1):
        start = format_time_str(ts['start'])
        end = format_time_str(ts['end'])
        srt_content += f"{start} --> {end}\n{ts['text']}\n\n"
    
    return result['text'], fig, srt_content

def format_time_str(seconds):
    """格式化时间显示"""
    return str(datetime.utcfromtimestamp(seconds).strftime('%H:%M:%S.%f'))[:-3]

# 创建Gradio界面
demo = gr.Interface(
    fn=visualize_timestamps,
    inputs=[
        gr.Audio(label="上传音频文件", type="filepath"),
        gr.Dropdown(["auto", "zh", "en", "yue", "ja", "ko"], 
                   label="语言选择", value="auto")
    ],
    outputs=[
        gr.Textbox(label="识别文本"),
        gr.Plot(label="时间轴可视化"),
        gr.Textbox(label="字幕文件内容", lines=10)
    ],
    title="语音识别时间戳可视化工具",
    description="上传音频文件,获取带时间戳的识别结果和可视化图表"
)

# 启动界面
demo.launch(server_name="0.0.0.0", server_port=7861)

6. 实战案例:会议录音分析与字幕生成

让我们通过一个完整的实战案例,将前面学到的知识串联起来。假设我们有一段30分钟的会议录音,需要完成以下任务:

  1. 转写会议内容
  2. 为每个发言添加时间戳
  3. 生成字幕文件
  4. 分析发言时间分布

6.1 完整处理流程

import os
from pathlib import Path
import json
from datetime import datetime

class MeetingAnalyzer:
    """会议录音分析器"""
    
    def __init__(self, model_path):
        """初始化分析器"""
        from funasr_onnx import SenseVoiceSmall
        self.model = SenseVoiceSmall(model_path, batch_size=1, quantize=True)
        
    def process_meeting(self, audio_path, output_dir="output"):
        """处理会议录音"""
        # 创建输出目录
        os.makedirs(output_dir, exist_ok=True)
        
        # 1. 语音识别(带时间戳)
        print("正在识别音频...")
        results = self.model([audio_path], 
                           language="auto",
                           use_itn=True,
                           return_timestamps=True,
                           timestamp_level="sentence")
        
        if not results:
            print("识别失败")
            return
        
        result = results[0]
        
        # 2. 保存原始结果
        raw_output = {
            "audio_file": audio_path,
            "total_duration": result.get('duration', 0),
            "language": result.get('language', 'unknown'),
            "full_text": result['text'],
            "sentences": result.get('sentences', []),
            "timestamp": datetime.now().isoformat()
        }
        
        raw_file = os.path.join(output_dir, "raw_result.json")
        with open(raw_file, 'w', encoding='utf-8') as f:
            json.dump(raw_output, f, indent=2, ensure_ascii=False)
        print(f"原始结果已保存: {raw_file}")
        
        # 3. 生成字幕文件
        srt_file = os.path.join(output_dir, "subtitles.srt")
        self.generate_subtitles(raw_output['sentences'], srt_file)
        print(f"字幕文件已生成: {srt_file}")
        
        # 4. 生成分析报告
        report_file = os.path.join(output_dir, "analysis_report.md")
        self.generate_report(raw_output, report_file)
        print(f"分析报告已生成: {report_file}")
        
        # 5. 可视化图表
        viz_file = os.path.join(output_dir, "timeline.png")
        self.visualize_timeline(raw_output['sentences'], viz_file)
        print(f"时间轴图表已生成: {viz_file}")
        
        return raw_output
    
    def generate_subtitles(self, sentences, output_file):
        """生成SRT字幕文件"""
        with open(output_file, 'w', encoding='utf-8') as f:
            for i, sent in enumerate(sentences, 1):
                start = self._format_srt_time(sent['start'])
                end = self._format_srt_time(sent['end'])
                f.write(f"{i}\n{start} --> {end}\n{sent['text']}\n\n")
    
    def _format_srt_time(self, seconds):
        """格式化SRT时间"""
        hours = int(seconds // 3600)
        minutes = int((seconds % 3600) // 60)
        secs = seconds % 60
        return f"{hours:02d}:{minutes:02d}:{secs:06.3f}".replace('.', ',')
    
    def generate_report(self, data, output_file):
        """生成分析报告"""
        sentences = data['sentences']
        total_duration = data['total_duration']
        
        # 计算统计信息
        num_sentences = len(sentences)
        avg_sentence_duration = sum(s['end'] - s['start'] for s in sentences) / num_sentences
        total_speech_duration = sum(s['end'] - s['start'] for s in sentences)
        
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write("# 会议录音分析报告\n\n")
            f.write(f"**分析时间**: {data['timestamp']}\n")
            f.write(f"**音频文件**: {data['audio_file']}\n")
            f.write(f"**识别语言**: {data['language']}\n\n")
            
            f.write("## 统计概览\n")
            f.write(f"- 音频总时长: {total_duration:.2f} 秒\n")
            f.write(f"- 识别句子数: {num_sentences} 句\n")
            f.write(f"- 总发言时长: {total_speech_duration:.2f} 秒\n")
            f.write(f"- 平均句长: {avg_sentence_duration:.2f} 秒/句\n")
            f.write(f"- 语音密度: {(total_speech_duration/total_duration*100):.1f}%\n\n")
            
            f.write("## 详细时间戳\n")
            f.write("| 序号 | 开始时间 | 结束时间 | 时长(秒) | 内容摘要 |\n")
            f.write("|------|----------|----------|-----------|----------|\n")
            
            for i, sent in enumerate(sentences, 1):
                duration = sent['end'] - sent['start']
                content_preview = sent['text'][:30] + "..." if len(sent['text']) > 30 else sent['text']
                f.write(f"| {i} | {sent['start']:.2f}s | {sent['end']:.2f}s | {duration:.2f} | {content_preview} |\n")
            
            f.write("\n## 完整文本\n")
            f.write(data['full_text'])
    
    def visualize_timeline(self, sentences, output_file):
        """生成时间轴可视化图表"""
        import matplotlib.pyplot as plt
        
        fig, ax = plt.subplots(figsize=(14, max(6, len(sentences)*0.3)))
        
        # 绘制时间块
        for i, sent in enumerate(sentences):
            start = sent['start']
            duration = sent['end'] - sent['start']
            ax.barh(i, duration, left=start, height=0.6, 
                   alpha=0.7, edgecolor='black')
            
            # 添加文本标签(缩短长文本)
            text = sent['text']
            if len(text) > 40:
                text = text[:37] + "..."
            ax.text(start + duration/2, i, text, 
                   ha='center', va='center', fontsize=9)
        
        ax.set_xlabel('时间 (秒)', fontsize=12)
        ax.set_ylabel('句子序号', fontsize=12)
        ax.set_title('会议发言时间分布', fontsize=14, pad=20)
        ax.grid(True, alpha=0.3, axis='x')
        
        plt.tight_layout()
        plt.savefig(output_file, dpi=150, bbox_inches='tight')
        plt.close()

# 使用示例
if __name__ == "__main__":
    # 初始化分析器
    analyzer = MeetingAnalyzer("/root/ai-models/danieldong/sensevoice-small-onnx-quant")
    
    # 处理会议录音
    result = analyzer.process_meeting(
        audio_path="meeting_recording.wav",
        output_dir="meeting_analysis"
    )
    
    print("处理完成!请在 meeting_analysis 目录查看结果文件。")

6.2 输出结果示例

运行上述代码后,你会得到以下文件:

  1. raw_result.json - 原始识别数据
  2. subtitles.srt - 可直接使用的字幕文件
  3. analysis_report.md - 详细的分析报告
  4. timeline.png - 时间轴可视化图表

分析报告内容示例

# 会议录音分析报告

**分析时间**: 2024-01-15T14:30:00
**音频文件**: meeting_recording.wav
**识别语言**: zh

## 统计概览
- 音频总时长: 1800.00 秒 (30分钟)
- 识别句子数: 45 句
- 总发言时长: 1420.50 秒
- 平均句长: 31.57 秒/句
- 语音密度: 78.9%

## 详细时间戳
| 序号 | 开始时间 | 结束时间 | 时长(秒) | 内容摘要 |
|------|----------|----------|-----------|----------|
| 1 | 0.00s | 12.50s | 12.50 | 大家好,欢迎参加本次季度... |
| 2 | 15.20s | 42.30s | 27.10 | 首先回顾一下上季度的业绩... |
| ... | ... | ... | ... | ... |

7. 实用技巧与常见问题

7.1 提升时间戳准确性的技巧

时间戳的准确性直接影响使用体验。以下是一些实用技巧:

1. 音频预处理很重要

import librosa
import soundfile as sf

def preprocess_audio(input_path, output_path):
    """音频预处理:标准化音量、降噪、格式转换"""
    # 读取音频
    y, sr = librosa.load(input_path, sr=16000)  # 重采样到16kHz
    
    # 音量标准化
    y_normalized = librosa.util.normalize(y)
    
    # 简单的降噪处理(可选)
    # y_denoised = librosa.effects.preemphasis(y_normalized)
    
    # 保存为WAV格式(兼容性最好)
    sf.write(output_path, y_normalized, sr, subtype='PCM_16')
    
    return output_path

# 使用预处理
clean_audio = preprocess_audio("noisy_recording.mp3", "cleaned_audio.wav")

2. 调整识别参数

# 更精细的时间戳设置
results = model(
    audio_files,
    language="zh",
    return_timestamps=True,
    timestamp_level="word",  # 词级别更精确
    vad=True,  # 启用语音活动检测
    vad_params={
        "threshold": 0.5,  # VAD阈值
        "min_silence_duration": 0.3,  # 最小静音时长
    }
)

3. 后处理优化

def refine_timestamps(sentences, min_gap=0.5):
    """优化时间戳:合并过短的间隔"""
    if not sentences:
        return sentences
    
    refined = [sentences[0]]
    
    for i in range(1, len(sentences)):
        prev = refined[-1]
        curr = sentences[i]
        
        # 如果间隔太短,合并句子
        if curr['start'] - prev['end'] < min_gap:
            prev['text'] += " " + curr['text']
            prev['end'] = curr['end']
        else:
            refined.append(curr)
    
    return refined

7.2 常见问题解答

Q: 时间戳不准确怎么办? A: 首先检查音频质量,背景噪音会影响准确性。可以尝试:

  1. 使用预处理函数清理音频
  2. 调整VAD参数(语音活动检测)
  3. 确保音频采样率为16kHz(模型最优采样率)

Q: 长音频处理时间戳偏移? A: 对于超过10分钟的音频,建议分段处理:

def process_long_audio(audio_path, chunk_duration=300):
    """分段处理长音频"""
    import librosa
    from pydub import AudioSegment
    
    audio = AudioSegment.from_file(audio_path)
    chunks = []
    
    # 按固定时长分段
    for i in range(0, len(audio), chunk_duration * 1000):
        chunk = audio[i:i + chunk_duration * 1000]
        chunk_path = f"chunk_{i//1000}.wav"
        chunk.export(chunk_path, format="wav")
        chunks.append(chunk_path)
    
    # 分别处理每个片段
    all_results = []
    time_offset = 0
    
    for chunk_file in chunks:
        result = model([chunk_file], return_timestamps=True)
        # 调整时间戳偏移
        for sentence in result[0]['sentences']:
            sentence['start'] += time_offset
            sentence['end'] += time_offset
        
        all_results.extend(result[0]['sentences'])
        time_offset += chunk_duration
    
    return all_results

Q: 如何支持更多音频格式? A: SenseVoice-small-onnx支持多种格式,但建议统一转为WAV处理:

def convert_to_wav(input_file, output_file=None):
    """转换音频格式为WAV"""
    from pydub import AudioSegment
    
    if output_file is None:
        output_file = input_file.rsplit('.', 1)[0] + '.wav'
    
    audio = AudioSegment.from_file(input_file)
    audio.export(output_file, format="wav", parameters=["-ar", "16000"])
    return output_file

Q: 时间戳数据如何导出到Excel?

import pandas as pd

def export_to_excel(timestamps, output_file="timestamps.xlsx"):
    """导出时间戳数据到Excel"""
    data = []
    for i, ts in enumerate(timestamps, 1):
        data.append({
            '序号': i,
            '开始时间(s)': ts['start'],
            '结束时间(s)': ts['end'],
            '时长(s)': ts['end'] - ts['start'],
            '内容': ts['text']
        })
    
    df = pd.DataFrame(data)
    df.to_excel(output_file, index=False)
    print(f"已导出到: {output_file}")

8. 总结

通过这篇教程,我们全面掌握了SenseVoice-small-onnx语音识别模型的时间戳对齐与可视化展示功能。从基础的环境部署,到时间戳数据的获取和处理,再到各种可视化展示方法,我们一步步构建了一个完整的语音识别工作流。

关键收获回顾

  1. 时间戳的价值:不仅仅是文字转写,更重要的是知道每个词、每句话在时间轴上的位置,这为后续的字幕生成、内容检索、会议分析等应用奠定了基础。

  2. 多种获取方式:无论是通过Web界面、REST API还是Python SDK,都能方便地获取带时间戳的识别结果,满足不同用户的需求。

  3. 丰富的可视化展示:从简单的字幕文件生成,到交互式的时间轴图表,再到完整的Web可视化工具,我们可以根据需求选择最合适的展示方式。

  4. 实战应用能力:通过会议录音分析的完整案例,我们学会了如何将时间戳功能应用到实际工作中,生成有价值的分析报告和可视化成果。

下一步建议

如果你已经掌握了基础的时间戳对齐,可以尝试以下进阶方向:

  • 结合说话人分离技术,为不同发言人生成独立的时间轴
  • 将时间戳数据与视频编辑软件结合,实现自动化字幕添加
  • 开发实时语音识别系统,动态展示时间戳信息
  • 结合情感分析,在时间轴上标注情感变化趋势

SenseVoice-small-onnx作为一个轻量高效的语音识别解决方案,在保持高精度的同时,提供了丰富的时间戳功能。无论是个人使用还是集成到企业应用中,都能显著提升语音内容处理的效率和质量。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐