- 1. 语音基础:把声音理解为数字
- 2. 语音识别(ASR):将话语转换为文字
- 3. 语音合成(TTS):将文字转换为声音
- 4. 说话人识别:谁在说话
- 5. 音乐 AI:AudioCraft 与创作性音频
- 6. 实时处理:流式 ASR 流水线
- 7. 实战应用:会议自动摘要系统
- 测验
- 结语
1. 语音基础:把声音理解为数字
声波与数字音频
声音是空气压力随时间的变化。麦克风将这种压力变化转换为电信号,ADC(模数转换器)按固定间隔采样,并存储为数字数组。
核心概念:
- 采样率(Sample Rate):每秒采样次数。要覆盖人类可听范围(20Hz~20kHz),根据奈奎斯特定理至少需要 40kHz。语音 AI 中 16kHz 是标准。
- 位深度(Bit Depth):每个采样点的精度。16-bit = 65,536 级,24-bit = 16,777,216 级。
- 声道:单声道(1 声道) vs 立体声(2 声道)。语音识别大多使用单声道 16kHz。
FFT 与频谱图
把时域波形转换为频域的 FFT(快速傅里叶变换),是音频分析的核心。
import librosa
import librosa.display
import numpy as np
import matplotlib.pyplot as plt
# 加载音频文件 (16kHz 单声道)
y, sr = librosa.load("speech.wav", sr=16000, mono=True)
print(f"样本数: {len(y)}, 采样率: {sr}Hz, 长度: {len(y)/sr:.2f}秒")
# STFT (短时傅里叶变换)
n_fft = 512 # FFT 窗口大小
hop_length = 128 # 窗口移动间隔
D = librosa.stft(y, n_fft=n_fft, hop_length=hop_length, window="hann")
magnitude = np.abs(D)
# 功率频谱图 (dB 刻度)
S_db = librosa.amplitude_to_db(magnitude, ref=np.max)
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
# 波形
axes[0].plot(np.linspace(0, len(y)/sr, len(y)), y)
axes[0].set_title("波形 (Waveform)")
axes[0].set_xlabel("时间 (秒)")
# 线性频谱图
librosa.display.specshow(S_db, sr=sr, hop_length=hop_length,
x_axis="time", y_axis="linear", ax=axes[1])
axes[1].set_title("线性频谱图 (Linear Spectrogram)")
# Mel 频谱图
mel_spec = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=80,
n_fft=n_fft, hop_length=hop_length)
mel_db = librosa.power_to_db(mel_spec, ref=np.max)
librosa.display.specshow(mel_db, sr=sr, hop_length=hop_length,
x_axis="time", y_axis="mel", ax=axes[2])
axes[2].set_title("Mel 频谱图 (80 Mel bins)")
plt.tight_layout()
plt.savefig("spectrogram_comparison.png", dpi=150)
MFCC:语音的压缩指纹
MFCC(梅尔频率倒谱系数, Mel-Frequency Cepstral Coefficients)是模拟人类听觉系统的特征。
处理步骤:
- 应用预加重(Pre-emphasis)滤波器 — 强调高频成分
- 分帧 + 加窗
- FFT → 功率谱
- 应用 Mel 滤波器组(将频率轴转换为 Mel 刻度)
- 取对数
- DCT(离散余弦变换) → MFCC 系数
import librosa
import numpy as np
def extract_mfcc_features(audio_path, sr=16000, n_mfcc=13, n_mels=40):
"""
MFCC 特征提取函数
返回:形状为 (39, T) 的 mfcc + delta + delta2 特征
"""
y, sr = librosa.load(audio_path, sr=sr)
# 预加重
y_emphasized = np.append(y[0], y[1:] - 0.97 * y[:-1])
# 提取 MFCC
mfcc = librosa.feature.mfcc(
y=y_emphasized, sr=sr,
n_mfcc=n_mfcc,
n_mels=n_mels,
n_fft=512,
hop_length=160, # 10ms (以 16kHz 为基准)
win_length=400, # 25ms
window="hann"
)
# Delta (一阶导数) — 动态特征
delta = librosa.feature.delta(mfcc)
# Delta-Delta (二阶导数)
delta2 = librosa.feature.delta(mfcc, order=2)
# 39 维特征向量 (13 + 13 + 13)
features = np.vstack([mfcc, delta, delta2])
# CMVN 归一化
features = (features - features.mean(axis=1, keepdims=True)) / \
(features.std(axis=1, keepdims=True) + 1e-8)
return features # shape: (39, T)
features = extract_mfcc_features("speech.wav")
print(f"MFCC 特征 shape: {features.shape}")
2. 语音识别(ASR):将话语转换为文字
CTC (联结主义时序分类, Connectionist Temporal Classification)
传统 ASR 是声学模型 + 语言模型 + 发音词典的三阶段流水线。CTC 革命性地降低了这种复杂度。
CTC 的核心思想:
- 输入序列(语音帧)与输出序列(文本)的长度互不相同
- 引入特殊的 blank 令牌解决对齐问题
- 通过对所有可能的对齐(alignment)概率求和进行训练(Forward-Backward 算法)
- 通过合并(collapse)解码去除连续相同标签与 blank
CTC 解码示例: a-a-blank-b → ab
Seq2Seq with Attention
基于 RNN 的 Seq2Seq 模型与 CTC 不同,内在地包含了语言模型。
- 编码器:用 BiLSTM/Transformer 把语音帧转换为上下文向量
- 注意力:在每个解码步骤决定关注编码器输出的哪一部分
- 解码器:用之前的输出令牌 + 注意力上下文生成下一个令牌
Whisper 架构完全解剖
OpenAI Whisper 是用 680,000 小时的多语言语音训练出来的编码器-解码器 Transformer。
架构细节:
- 音频编码器:30 秒分块 → 80 通道 log-Mel 频谱图 → 2 个 Conv1D → Transformer 编码器
- 文本解码器:通过交叉注意力参照音频上下文 → 自回归生成
- 特殊令牌:语言令牌、任务令牌(transcribe/translate)、时间戳令牌
import whisper
import json
def transcribe_with_timestamps(audio_path, model_size="large-v3", language="ko"):
"""
使用 Whisper 进行带时间戳的转写
模型大小: tiny, base, small, medium, large, large-v3
"""
model = whisper.load_model(model_size)
result = model.transcribe(
audio_path,
language=language,
task="transcribe", # 改为 "translate" 则翻译为英语
word_timestamps=True, # 按单词的时间戳
condition_on_previous_text=True,
temperature=0,
beam_size=5,
verbose=False
)
print(f"完整转写: {result['text']}")
print(f"检测到的语言: {result['language']}")
for seg in result["segments"]:
start = f"{seg['start']:.2f}s"
end = f"{seg['end']:.2f}s"
text = seg["text"].strip()
print(f"[{start} -> {end}] {text}")
if "words" in seg:
for word in seg["words"]:
ws = f"{word['start']:.2f}s"
we = f"{word['end']:.2f}s"
print(f" {word['word']}: {ws}~{we}")
return result
result = transcribe_with_timestamps("meeting.wav", model_size="large-v3", language="ko")
with open("transcript.json", "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
韩语/日语 ASR 的特殊性
韩语 ASR 的挑战:
- 黏着语:词尾变化极为多样(먹다/먹어/먹었다/먹겠다)
- 连音现象:"국어" 发音为 [구거]
- 硬音化/送气音化:需要处理复杂的音变规则
- 分写(空格)不规则,需要后处理
日语 ASR 的挑战:
- 平假名、片假名、汉字三种文字体系混用
- 长元音/短元音的区分(오코리, 오코리이)
- 元音清化现象(す、き 等假名的不发声化)
3. 语音合成(TTS):将文字转换为声音
Tacotron 2 → FastSpeech 2 → VITS 的演进
| 模型 | 架构 | 特点 | 推理速度 |
|---|---|---|---|
| Tacotron 2 | Seq2Seq + Attention | 韵律自然,推理较慢 | 慢 |
| FastSpeech 2 | 非自回归 Transformer | 显式 duration/pitch/energy | 快 |
| VITS | VAE + Normalizing Flow + GAN | 单一模型,音质最佳 | 中等 |
FastSpeech 2 合成实战
from TTS.api import TTS
import torch
def synthesize_speech_ko(text, output_path, speed=1.0):
"""
使用 Coqui TTS 进行韩语语音合成
模型: tts_models/ko/css10/vits
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
tts = TTS("tts_models/ko/css10/vits").to(device)
tts.tts_to_file(
text=text,
file_path=output_path,
speed=speed,
)
print(f"合成完成: {output_path}")
# 韩语 TTS 示例
text_ko = "안녕하세요. 음성 AI 기술이 놀랍도록 발전했습니다."
synthesize_speech_ko(text_ko, "output_ko.wav")
# 多语言 TTS (XTTS-v2)
def synthesize_multilang(text, language, speaker_wav, output_path):
"""
XTTS-v2: 说话人语音克隆 + 多语言合成
language: "ko", "ja", "en", "zh-cn" 等
speaker_wav: 3~6 秒参考语音
"""
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2")
tts.tts_to_file(
text=text,
language=language,
speaker_wav=speaker_wav,
file_path=output_path,
)
synthesize_multilang(
"목소리를 복제하여 다국어로 말할 수 있습니다.",
language="ko",
speaker_wav="reference_voice.wav",
output_path="cloned_ko.wav"
)
VITS:端到端语音合成的革新
VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech,面向端到端 TTS 的对抗学习变分推断)直接从文本生成波形。
核心组成:
- Posterior Encoder(后验编码器):目标音频 → 潜在变量 z
- Prior Encoder(先验编码器):文本 → 先验分布(包含 normalizing flow)
- Decoder(解码器, HiFi-GAN):潜在变量 z → 波形
- Stochastic Duration Predictor(随机时长预测器):预测每个音素的持续时间
Normalizing flow 把复杂的语音分布转换为简单的高斯分布,从而提升训练稳定性。相比 Tacotron 2,VITS 支持并行推理,且不需要外部声码器。
4. 说话人识别:谁在说话
x-vector 与 ECAPA-TDNN
i-vector(传统方法):
- 基于 GMM-UBM 的总变异性空间建模
- 生成固定长度的说话人嵌入
- 特征较浅,判别能力有限
x-vector(深度学习方法):
- 基于 TDNN(时延神经网络, Time Delay Neural Network)
- 帧级特征 → 统计池化 → 段级嵌入
- 用 PLDA(概率线性判别分析)打分
ECAPA-TDNN(强调通道注意力、传播与聚合):
- 多尺度特征聚合
- 通过通道注意力机制提升说话人区分能力
- 2020 年 VoxCeleb 挑战赛冠军
用 pyannote.audio 做说话人分离(Diarization)
from pyannote.audio import Pipeline
import torch
def speaker_diarization(audio_path, hf_token, num_speakers=None):
"""
说话人分离: 识别谁在何时说话
"""
pipeline = Pipeline.from_pretrained(
"pyannote/speaker-diarization-3.1",
use_auth_token=hf_token
)
if torch.cuda.is_available():
pipeline = pipeline.to(torch.device("cuda"))
params = {}
if num_speakers:
params["num_speakers"] = num_speakers
else:
params["min_speakers"] = 1
params["max_speakers"] = 10
diarization = pipeline(audio_path, **params)
segments = []
for turn, _, speaker in diarization.itertracks(yield_label=True):
seg = {
"start": round(turn.start, 3),
"end": round(turn.end, 3),
"speaker": speaker
}
segments.append(seg)
print(f"[{seg['start']:.3f}s ~ {seg['end']:.3f}s] {speaker}")
with open("diarization.rttm", "w") as rttm:
diarization.write_rttm(rttm)
speakers_found = len(set(s["speaker"] for s in segments))
print(f"\n共检测到 {speakers_found} 位说话人")
return segments
segments = speaker_diarization("meeting.wav", hf_token="YOUR_HF_TOKEN")
结合 Whisper + pyannote:按说话人转写
import whisper
from pyannote.audio import Pipeline
import torch
def diarize_and_transcribe(audio_path, hf_token, language="ko"):
"""说话人分离 + 语音识别结合"""
# 1. 说话人分离
diarization_pipeline = Pipeline.from_pretrained(
"pyannote/speaker-diarization-3.1",
use_auth_token=hf_token
)
diarization = diarization_pipeline(audio_path)
# 2. Whisper 转写
asr_model = whisper.load_model("large-v3")
asr_result = asr_model.transcribe(audio_path, language=language,
word_timestamps=True)
# 3. 单词时间戳与说话人信息映射
words_with_speakers = []
for seg in asr_result["segments"]:
for word in seg.get("words", []):
word_mid = (word["start"] + word["end"]) / 2
speaker = "UNKNOWN"
for turn, _, spk in diarization.itertracks(yield_label=True):
if turn.start <= word_mid <= turn.end:
speaker = spk
break
words_with_speakers.append({
"word": word["word"],
"start": word["start"],
"end": word["end"],
"speaker": speaker
})
# 4. 按说话人分组语句
grouped = []
current_speaker = None
current_text = []
for item in words_with_speakers:
if item["speaker"] != current_speaker:
if current_text:
grouped.append({
"speaker": current_speaker,
"text": "".join(current_text).strip()
})
current_speaker = item["speaker"]
current_text = [item["word"]]
else:
current_text.append(item["word"])
if current_text:
grouped.append({"speaker": current_speaker,
"text": "".join(current_text).strip()})
for entry in grouped:
print(f"[{entry['speaker']}]: {entry['text']}")
return grouped
5. 音乐 AI:AudioCraft 与创作性音频
用 MusicGen 生成音乐
Meta 的 AudioCraft 包含用文字描述生成音乐的 MusicGen,以及生成一般音频的 AudioGen。
from audiocraft.models import MusicGen
from audiocraft.data.audio import audio_write
import torch
def generate_music(descriptions, duration=10, model_size="medium"):
"""
根据文字描述生成音乐
模型大小: small (300M), medium (1.5B), large (3.3B), melody
"""
model = MusicGen.get_pretrained(f"facebook/musicgen-{model_size}")
model.set_generation_params(
duration=duration, # 以秒为单位 (最长 30 秒)
temperature=1.0, # 控制创造性
top_k=250,
cfg_coef=3.0, # Classifier-Free Guidance 强度
)
wav = model.generate(descriptions)
# shape: (batch, channels, samples)
for i, (desc, audio) in enumerate(zip(descriptions, wav)):
audio_write(
f"music_{i}",
audio.cpu(),
model.sample_rate,
strategy="loudness",
loudness_compressor=True
)
print(f"生成完成: music_{i}.wav")
print(f" 描述: {desc}")
# 生成多种风格
descriptions = [
"Upbeat K-pop with synth leads, 120 BPM, energetic and bright",
"Calm Korean traditional music with gayageum and daegeum, peaceful",
"Epic orchestral trailer music with powerful drums and brass",
"Lo-fi hip hop beats with jazz piano, for studying",
]
generate_music(descriptions, duration=15, model_size="medium")
用 AudioGen 生成音效
from audiocraft.models import AudioGen
from audiocraft.data.audio import audio_write
def generate_sound_effects(descriptions, duration=5):
"""生成环境音、音效"""
model = AudioGen.get_pretrained("facebook/audiogen-medium")
model.set_generation_params(duration=duration, temperature=1.0)
wav = model.generate(descriptions)
for i, (desc, audio) in enumerate(zip(descriptions, wav)):
audio_write(f"sfx_{i}", audio.cpu(), model.sample_rate)
print(f"生成: sfx_{i}.wav — {desc}")
sound_descriptions = [
"Rain falling on a tin roof with distant thunder",
"Busy city street with cars and people talking",
"Birds chirping in a forest at dawn",
"Keyboard typing in a quiet office",
]
generate_sound_effects(sound_descriptions, duration=8)
用 Demucs 做音乐分离
import subprocess
import os
def separate_audio_tracks(audio_path, output_dir="separated", model="htdemucs"):
"""
将音乐分离为人声/鼓/贝斯/吉他等
模型: htdemucs (4-stem), htdemucs_6s (6-stem)
"""
os.makedirs(output_dir, exist_ok=True)
cmd = [
"python", "-m", "demucs",
"--name", model,
"--out", output_dir,
"--mp3",
"--mp3-bitrate", "320",
audio_path
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
base_name = os.path.splitext(os.path.basename(audio_path))[0]
stems_dir = os.path.join(output_dir, model, base_name)
stems = os.listdir(stems_dir)
print(f"分离成功!音轨: {stems}")
# htdemucs: vocals.mp3, drums.mp3, bass.mp3, other.mp3
# htdemucs_6s: + guitar.mp3, piano.mp3
else:
print(f"错误: {result.stderr}")
return output_dir
separate_audio_tracks("song.mp3", model="htdemucs_6s")
6. 实时处理:流式 ASR 流水线
look-ahead window 在流式 ASR 中带来一个重要的权衡。看到的未来语音越多,准确率越高,但延迟也会随之增加。
import sounddevice as sd
import numpy as np
import whisper
import queue
import threading
from collections import deque
class RealTimeASR:
"""
实时流式语音识别
通过 look-ahead window 调节准确率/延迟的平衡
"""
def __init__(self, model_size="small", language="ko",
chunk_duration=2.0, look_ahead=0.5, sample_rate=16000):
self.model = whisper.load_model(model_size)
self.language = language
self.chunk_duration = chunk_duration
self.look_ahead = look_ahead
self.sample_rate = sample_rate
self.audio_queue = queue.Queue()
max_buf = int(sample_rate * (chunk_duration + look_ahead) * 3)
self.buffer = deque(maxlen=max_buf)
self.is_running = False
def audio_callback(self, indata, frames, time, status):
if status:
print(f"音频状态: {status}")
self.audio_queue.put(indata.copy())
def process_audio(self):
chunk_samples = int(self.sample_rate * self.chunk_duration)
while self.is_running:
audio_chunk = []
while len(audio_chunk) < chunk_samples:
try:
data = self.audio_queue.get(timeout=0.1)
audio_chunk.extend(data.flatten())
except queue.Empty:
break
if len(audio_chunk) < chunk_samples // 2:
continue
self.buffer.extend(audio_chunk)
audio_array = np.array(list(self.buffer), dtype=np.float32)
audio_array = audio_array / (np.max(np.abs(audio_array)) + 1e-8)
result = self.model.transcribe(
audio_array,
language=self.language,
temperature=0,
no_speech_threshold=0.6,
)
if result["text"].strip():
print(f"\r识别: {result['text'].strip()}", end="", flush=True)
def start(self):
self.is_running = True
t = threading.Thread(target=self.process_audio, daemon=True)
t.start()
print("麦克风已激活 — 请说话 (Ctrl+C 结束)")
with sd.InputStream(
samplerate=self.sample_rate,
channels=1,
dtype="float32",
blocksize=int(self.sample_rate * 0.1),
callback=self.audio_callback
):
try:
while True:
sd.sleep(100)
except KeyboardInterrupt:
self.is_running = False
print("\n识别结束")
# 运行
asr = RealTimeASR(model_size="small", language="ko",
chunk_duration=2.0, look_ahead=0.5)
asr.start()
7. 实战应用:会议自动摘要系统
import whisper
from pyannote.audio import Pipeline
from openai import OpenAI
def auto_meeting_summary(audio_path, hf_token, openai_key, language="ko"):
"""
会议自动摘要: 说话人分离 + 转写 + LLM 摘要
"""
client = OpenAI(api_key=openai_key)
# 1. 说话人分离
print("正在进行说话人分离...")
diarization_pipeline = Pipeline.from_pretrained(
"pyannote/speaker-diarization-3.1",
use_auth_token=hf_token
)
diarization = diarization_pipeline(audio_path)
# 2. 转写
print("正在进行语音识别...")
asr_model = whisper.load_model("large-v3")
result = asr_model.transcribe(audio_path, language=language,
word_timestamps=True)
# 3. 说话人-文本映射及对话记录构建
lines = []
current_speaker = None
current_words = []
for seg in result["segments"]:
for word in seg.get("words", []):
mid = (word["start"] + word["end"]) / 2
speaker = "UNKNOWN"
for turn, _, spk in diarization.itertracks(yield_label=True):
if turn.start <= mid <= turn.end:
speaker = spk
break
if speaker != current_speaker:
if current_words:
lines.append(f"[{current_speaker}]: {''.join(current_words).strip()}")
current_speaker = speaker
current_words = [word["word"]]
else:
current_words.append(word["word"])
if current_words:
lines.append(f"[{current_speaker}]: {''.join(current_words).strip()}")
transcript = "\n".join(lines)
# 4. LLM 摘要
print("正在生成摘要...")
prompt = f"""请将以下会议转写记录按下列条目进行摘要:
1. 主要讨论事项
2. 已决定事项
3. Action Items (含负责人)
4. 下次会议安排
转写记录:
{transcript[:4000]}"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0.3
)
summary = response.choices[0].message.content
print("\n=== 会议摘要 ===")
print(summary)
return {"transcript": transcript, "summary": summary}
测验
Q1. CTC(联结主义时序分类, Connectionist Temporal Classification) loss 如何在没有对齐信息的情况下实现序列学习?
答案:CTC 通过对输入帧序列与输出标签序列之间所有可能的对齐(单调对齐, monotonic alignment)的概率求和来进行训练。
解析:当从语音的 T 个帧生成 N 个文本标签时,每一帧都会输出一个标签或 blank 令牌。通过去除相同标签的连续重复及 blank 的合并(collapse)解码规则来还原最终文本。Forward-Backward 算法(动态规划)能高效计算所有对齐的概率之和。这样一来,无需音素边界或对齐标注,仅凭文本标签即可完成训练。
Q2. 为什么 Mel 频谱图从听觉感知角度比线性频谱图更适合语音识别?
答案:人类的听觉系统对频率的感知呈对数尺度,而 Mel 刻度正反映了这一点。
解析:人耳的耳蜗(cochlea)在低频区域能更精细地区分频率,而在高频区域相对迟钝。Mel 滤波器组通过在低频区域密集布置滤波器、在高频区域稀疏布置来模拟这一特性。结果是,80 个 Mel bin 比 512 个线性 bin 能更紧凑、更有效地表达与语音相关的信息,使模型能更好地学习语音的声学特性。
Q3. 为什么 VITS 比 Tacotron 2 更适合做端到端训练(与 normalizing flow 相关)?
答案:VITS 通过 normalizing flow 直接建模文本与音频之间复杂的概率分布,从而无需中间表示(mel spectrogram)即可用单一模型完成训练。
解析:Tacotron 2 是文本 → mel spectrogram → 波形的两阶段流水线,每个阶段的误差都会累积。VITS 将 VAE 的潜在空间通过 normalizing flow 转换。Normalizing flow 是一条可逆函数链,能将简单的高斯分布精确转换为复杂的语音分布,反向也能精确计算似然。由此,文本条件先验分布与音频后验分布通过 flow 直接相连,实现了完整的端到端训练。
Q4. 为什么 x-vector 说话人嵌入比 i-vector 更适合用深度学习训练?
答案:x-vector 通过判别式(discriminative)学习直接优化说话人之间的决策边界,而 i-vector 基于生成式(generative)建模。
解析:i-vector 用 GMM-UBM 统计量与总变异性矩阵对说话人空间建模,并未显式优化用于说话人识别的判别边界。x-vector(基于 TDNN)通过 softmax 交叉熵直接学习说话人分类,因此说话人之间的决策边界更清晰。TDNN 的统计池化层将可变长度的语音转换为固定大小的嵌入,深度学习的非线性能捕捉复杂的说话人声学模式。数据充足时,x-vector 在 EER(等错误率, Equal Error Rate)上明显优于 i-vector。
Q5. 在流式 ASR 中,look-ahead window 在识别准确率与延迟之间造成怎样的权衡?
答案:look-ahead window 越大,越能利用未来上下文提升准确率,但结果输出的延迟(latency)也会随之增加。
解析:语音的时间上下文很重要。"배"这个词,是"배가 고프다(肚子饿)"里的"肚子",还是"배를 타다(坐船)"里的"船",要靠后面出现的词才能判断。look-ahead 为 0 时只看当前块,延迟最小,但边界部分的识别错误会增加。look-ahead 越长,准确率越高,但结果输出也越滞后。在实时字幕系统中,200~500ms 的 look-ahead 是实用的平衡点。CIF(Continuous Integrate-and-Fire)或 Emformer 等架构从结构上改善了这一权衡。
结语
语音 AI 是 ASR、TTS、说话人识别、音乐生成彼此关联的广阔而深邃的领域。从 Whisper 的多语言转写,到 pyannote 的说话人分离、VITS 的自然语音合成,直至 MusicGen 的音乐创作——今天介绍的这些工具全部都是开源可用的。将各个组件组合起来,就可以构建呼叫中心 AI、会议摘要、实时翻译、无障碍工具等各种实战系统。
현재 단락 (1/461)
声音是空气压力随时间的变化。麦克风将这种压力变化转换为电信号,ADC(模数转换器)按固定间隔采样,并存储为数字数组。