AI音乐与语音生成完全教程(Suno/Udio/Bark)

教程简介

本教程全面讲解AI音乐与语音生成技术,涵盖Suno/Udio/Bark/MusicGen/XTTS等主流工具对比、自回归与扩散模型的音乐生成原理、歌词到完整歌曲的生成Pipeline、语音合成TTS技术(传统/神经网络/零样本克隆)、语音克隆与声音定制、音频风格迁移、音乐质量评估标准,以及版权与伦理问题。包含完整的Python代码示例和MusicGen/Bark实战案例,适合开发者深入学习AI音频生成技术。

AI音乐与语音生成完全教程(Suno/Udio/Bark)

一、概述:AI音频生成技术的发展历程

1.1 从信号处理到深度学习

音频生成技术的发展经历了从传统信号处理到深度学习的范式转变。早期的语音合成主要依赖于**拼接合成(Concatenative Synthesis)参数合成(Parametric Synthesis)**方法,而音乐生成则受限于规则系统和马尔可夫链等统计方法。

深度学习的引入彻底改变了这一格局:

第一阶段:统计参数合成(2013-2016)

  • DNN-HMM混合系统:使用深度神经网络替代高斯混合模型
  • WaveNet(DeepMind, 2016):首次使用自回归神经网络直接生成波形,质量突破性提升

第二阶段:端到端神经网络(2017-2021)

  • Tacotron/Tacotron 2(Google, 2017/2018):端到端文本到频谱图生成
  • WaveRNN/WaveGlow:快速神经声码器
  • Transformer TTS:引入注意力机制
  • Jukebox(OpenAI, 2020):基于VQ-VAE的音乐生成

第三阶段:大规模预训练与多模态(2022-至今)

  • Bark(Suno, 2023):多语言TTS,支持笑声、叹息等非语言声音
  • MusicGen(Meta, 2023):单阶段自回归音乐生成
  • Suno(2023-2024):端到端音乐生成,支持歌词到完整歌曲
  • Udio(2024):高品质AI音乐生成
  • XTTS(Coqui, 2023):零样本语音克隆

1.2 当前技术格局

公司/团队 产品 类型 特点
Suno Suno v3.5 音乐生成 歌词到完整歌曲,多种风格
Udio Udio v1.5 音乐生成 高品质,精细控制
Meta MusicGen 音乐生成 开源,单阶段生成
Suno Bark 语音生成 多语言,情感丰富
Coqui XTTS 语音克隆 零样本克隆,开源
ElevenLabs ElevenLabs 语音合成 最高品质,商业API
Google MusicLM/MusicFX 音乐生成 高品质,DJ模式

1.3 音频生成的核心挑战

  1. 长程依赖:音乐和语音都具有长时间跨度的结构关系
  2. 多尺度建模:需要同时处理采样点级别(波形)、帧级别(频谱)和段级别(结构)
  3. 主观评估:音频质量难以用客观指标完全衡量
  4. 实时性要求:语音合成通常需要实时或超实时生成
  5. 多样性与可控性平衡:既要丰富多样又要精确控制

二、主流工具对比与选择指南

2.1 Suno

核心特性:

  • 端到端音乐生成:输入歌词或描述,输出完整歌曲(含人声)
  • 支持多种音乐风格:流行、摇滚、电子、古典、说唱等
  • 中文歌词支持良好
  • 每首歌最长约4分钟
  • 提供Web界面和API

API使用示例:

import requests
import json
import time

class SunoClient:
    """Suno API客户端"""
    
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.suno.ai/v1"
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
    
    def generate_music(self, prompt, lyrics=None, style=None,
                       title=None, instrumental=False):
        """
        生成音乐
        
        参数:
            prompt: 音乐描述
            lyrics: 歌词(可选)
            style: 音乐风格(可选)
            title: 标题(可选)
            instrumental: 是否纯音乐
        """
        payload = {
            "prompt": prompt,
            "make_instrumental": instrumental
        }
        
        if lyrics:
            payload["lyrics"] = lyrics
        if style:
            payload["style"] = style
        if title:
            payload["title"] = title
        
        response = requests.post(
            f"{self.base_url}/generate",
            json=payload,
            headers=self.headers
        )
        
        return response.json()
    
    def get_generation(self, generation_id):
        """查询生成状态"""
        response = requests.get(
            f"{self.base_url}/generate/{generation_id}",
            headers=self.headers
        )
        return response.json()
    
    def wait_for_completion(self, generation_id, timeout=300):
        """等待生成完成"""
        start_time = time.time()
        
        while time.time() - start_time < timeout:
            result = self.get_generation(generation_id)
            
            if result.get("status") == "complete":
                return result
            elif result.get("status") == "failed":
                raise Exception(f"生成失败: {result.get('error')}")
            
            print(f"生成中... 状态: {result.get('status')}")
            time.sleep(5)
        
        raise TimeoutError("生成超时")


# 使用示例
client = SunoClient("your_api_key")

# 示例1:带歌词的歌曲生成
result = client.generate_music(
    prompt="A cheerful pop song about summer adventures",
    lyrics="""[Verse 1]
阳光洒在海面上
我们奔跑在沙滩上
青春的歌声随风飘荡
这一刻永远不会忘

[Chorus]
夏日的风 吹过脸庞
我们的故事 正在发光
每一刻都是最好的时光
让我们一起 奔向远方

[Verse 2]
星空下许下愿望
明天会更加闪亮
不管前方有多少风浪
我们都要勇敢去闯""",
    style="upbeat pop, summer vibes, acoustic guitar",
    title="夏日冒险"
)

print(json.dumps(result, indent=2, ensure_ascii=False))

# 等待完成
completed = client.wait_for_completion(result["id"])
print(f"歌曲已生成: {completed.get('audio_url')}")

2.2 Udio

核心特性:

  • 高品质音乐生成,音质业界领先
  • 支持精细的风格控制
  • 可生成长达15分钟的音乐
  • 支持音频扩展和变体生成
  • 提供Web界面和API

API使用示例:

import requests
import time

class UdioClient:
    """Udio API客户端"""
    
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.udio.com/v1"
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
    
    def generate(self, prompt, duration=30, style_tags=None,
                 seed=None, lyrics=None):
        """
        生成音乐
        
        参数:
            prompt: 音乐描述
            duration: 时长(秒),最多900秒(15分钟)
            style_tags: 风格标签列表
            seed: 随机种子
            lyrics: 歌词(可选)
        """
        payload = {
            "prompt": prompt,
            "duration": min(duration, 900),
        }
        
        if style_tags:
            payload["style_tags"] = style_tags
        if seed is not None:
            payload["seed"] = seed
        if lyrics:
            payload["lyrics"] = lyrics
        
        response = requests.post(
            f"{self.base_url}/generate",
            json=payload,
            headers=self.headers
        )
        return response.json()
    
    def extend(self, audio_id, prompt, duration=30):
        """
        扩展已有音乐
        
        参数:
            audio_id: 原始音频ID
            prompt: 扩展描述
            duration: 扩展时长
        """
        payload = {
            "audio_id": audio_id,
            "prompt": prompt,
            "duration": duration,
            "mode": "extend"  # extend, inpaint, remix
        }
        
        response = requests.post(
            f"{self.base_url}/edit",
            json=payload,
            headers=self.headers
        )
        return response.json()
    
    def remix(self, audio_id, prompt, strength=0.5):
        """
        重新混音
        """
        payload = {
            "audio_id": audio_id,
            "prompt": prompt,
            "strength": strength,
            "mode": "remix"
        }
        
        response = requests.post(
            f"{self.base_url}/edit",
            json=payload,
            headers=self.headers
        )
        return response.json()


# 使用示例
client = UdioClient("your_api_key")

# 生成一首电子音乐
result = client.generate(
    prompt="Energetic electronic dance music with heavy bass drops, "
           "synth leads, and euphoric builds",
    duration=60,
    style_tags=["EDM", "electronic", "dance", "festival"],
    seed=42
)

print(f"生成任务ID: {result['id']}")

2.3 Bark(Suno开源TTS)

核心特性:

  • 完全开源,可本地部署
  • 支持多语言(中、英、日、韩等13种语言)
  • 能够生成非语言声音(笑声、叹息、哭泣、音乐等)
  • 支持说话人预设
  • 基于GPT风格的自回归架构

本地部署与使用:

# 安装: pip install git+https://github.com/suno-ai/bark.git

import torch
from bark import SAMPLE_RATE, generate_audio, preload_models
from scipy.io.wavfile import write as write_wav
import numpy as np

class BarkTTS:
    """Bark语音合成器"""
    
    # 可用的说话人预设
    SPEAKER_PRESETS = {
        "中文女声": "v2/zh_speaker_6",
        "中文男声": "v2/zh_speaker_2",
        "英文女声": "v2/en_speaker_6",
        "英文男声": "v2/en_speaker_2",
        "日文女声": "v2/ja_speaker_4",
        "韩文女声": "v2/ko_speaker_3",
    }
    
    def __init__(self, device="cuda"):
        self.device = device
        print("正在加载Bark模型...")
        preload_models()
        print("模型加载完成")
    
    def generate(self, text, speaker="v2/zh_speaker_6",
                 output_path="output.wav"):
        """
        生成语音
        
        参数:
            text: 输入文本
            speaker: 说话人预设
            output_path: 输出路径
        """
        audio_array = generate_audio(
            text,
            history_prompt=speaker,
            text_temp=0.7,
            waveform_temp=0.7
        )
        
        # 保存音频
        write_wav(output_path, SAMPLE_RATE, audio_array)
        print(f"音频已保存: {output_path}")
        
        return audio_array
    
    def generate_with_emotion(self, text, emotion="neutral",
                               speaker="v2/zh_speaker_6"):
        """
        带情感的语音生成
        通过在文本中添加特殊标记来控制情感
        """
        # Bark支持特殊标记来控制非语言声音
        emotion_markers = {
            "笑": "[laugh]",
            "叹气": "[sigh]",
            "哭泣": "[gasps]",
            "犹豫": "...",
            "惊喜": "[clears throat]",
            "歌唱": "♪ ",  # 在文本前添加音符符号可触发歌唱模式
        }
        
        # 构建带情感的文本
        if emotion in emotion_markers:
            enhanced_text = f"{emotion_markers[emotion]} {text}"
        else:
            enhanced_text = text
        
        return self.generate(enhanced_text, speaker)
    
    def generate_singing(self, lyrics, speaker="v2/zh_speaker_6"):
        """
        生成歌唱音频
        在文本前添加♪符号可触发歌唱模式
        """
        singing_text = f"♪ {lyrics} ♪"
        return self.generate(singing_text, speaker)
    
    def generate_dialogue(self, lines, speakers=None):
        """
        生成多说话人对话
        
        参数:
            lines: 对话列表 [{"text": "...", "speaker": "..."}]
            speakers: 说话人映射
        """
        if speakers is None:
            speakers = {
                "A": "v2/zh_speaker_6",
                "B": "v2/zh_speaker_2"
            }
        
        all_audio = []
        
        for line in lines:
            speaker = speakers.get(line.get("speaker", "A"),
                                    "v2/zh_speaker_6")
            audio = generate_audio(
                line["text"],
                history_prompt=speaker,
                text_temp=0.7,
                waveform_temp=0.7
            )
            all_audio.append(audio)
            # 添加短暂停顿
            silence = np.zeros(int(SAMPLE_RATE * 0.3))
            all_audio.append(silence)
        
        # 合并所有音频
        combined = np.concatenate(all_audio)
        return combined
    
    def batch_generate(self, texts, speaker="v2/zh_speaker_6",
                       output_dir="outputs"):
        """批量生成语音"""
        import os
        os.makedirs(output_dir, exist_ok=True)
        
        results = []
        for i, text in enumerate(texts):
            output_path = os.path.join(output_dir, f"audio_{i:04d}.wav")
            try:
                audio = self.generate(text, speaker, output_path)
                results.append({
                    "text": text,
                    "path": output_path,
                    "status": "success"
                })
            except Exception as e:
                results.append({
                    "text": text,
                    "error": str(e),
                    "status": "failed"
                })
        
        return results


# 使用示例
tts = BarkTTS()

# 基础语音生成
tts.generate(
    "欢迎来到AI音频生成的世界,这是一个令人兴奋的领域!",
    speaker="v2/zh_speaker_6",
    output_path="welcome.wav"
)

# 带情感的语音
tts.generate_with_emotion(
    "哇,这个结果太棒了!",
    emotion="惊喜",
    speaker="v2/zh_speaker_6"
)

# 歌唱模式
tts.generate_singing(
    "让我们一起探索AI的奇妙世界",
    speaker="v2/zh_speaker_6"
)

# 多人对话
dialogue = [
    {"text": "你好,今天天气真好!", "speaker": "A"},
    {"text": "是啊,我们去公园走走吧。", "speaker": "B"},
    {"text": "好主意,我马上准备一下。", "speaker": "A"},
]
combined_audio = tts.generate_dialogue(dialogue)
write_wav("dialogue.wav", SAMPLE_RATE, combined_audio)

2.4 MusicGen(Meta开源音乐生成)

核心特性:

  • 完全开源,Meta AI出品
  • 单阶段自回归Transformer架构
  • 支持文本描述和旋律条件输入
  • 可生成30秒高品质音乐
  • 多种模型尺寸(300M/1.5B/3.3B)

本地部署与使用:

# 安装: pip install transformers torch audiocraft

from transformers import AutoProcessor, MusicgenForConditionalGeneration
import torch
import scipy.io.wavfile as wavfile
import numpy as np

class MusicGenClient:
    """MusicGen音乐生成客户端"""
    
    MODEL_SIZES = {
        "small": "facebook/musicgen-small",    # 300M参数
        "medium": "facebook/musicgen-medium",   # 1.5B参数
        "large": "facebook/musicgen-large",     # 3.3B参数
    }
    
    def __init__(self, model_size="medium", device="cuda"):
        """
        初始化MusicGen
        
        参数:
            model_size: 模型大小 (small/medium/large)
            device: 计算设备
        """
        model_id = self.MODEL_SIZES[model_size]
        print(f"加载模型: {model_id}")
        
        self.processor = AutoProcessor.from_pretrained(model_id)
        self.model = MusicgenForConditionalGeneration.from_pretrained(model_id)
        self.model.to(device)
        self.device = device
        
        print("模型加载完成")
    
    def generate(self, prompt, duration=8, num_samples=1,
                 guidance_scale=3.0, temperature=1.0):
        """
        从文本生成音乐
        
        参数:
            prompt: 音乐描述
            duration: 时长(秒),最大30秒
            num_samples: 生成样本数
            guidance_scale: 引导强度(值越大越符合提示词)
            temperature: 温度(值越大越多样)
        """
        inputs = self.processor(
            text=[prompt] * num_samples,
            padding=True,
            return_tensors="pt"
        ).to(self.device)
        
        # 计算最大生成token数
        # MusicGen: 50 tokens/秒(对于stereo_44khz)
        max_new_tokens = int(duration * 50)
        
        audio_values = self.model.generate(
            **inputs,
            max_new_tokens=min(max_new_tokens, 1500),
            guidance_scale=guidance_scale,
            temperature=temperature,
            do_sample=True,
        )
        
        # 转换为numpy数组
        audio_array = audio_values.cpu().numpy()
        
        return audio_array
    
    def generate_with_melody(self, prompt, melody_path,
                              duration=8, guidance_scale=3.0):
        """
        使用旋律条件生成音乐
        
        参数:
            prompt: 音乐描述
            melody_path: 参考旋律音频路径
            duration: 时长
        """
        import librosa
        
        # 加载参考旋律
        melody, sr = librosa.load(melody_path, sr=32000)
        melody = torch.from_numpy(melody).unsqueeze(0).to(self.device)
        
        inputs = self.processor(
            text=[prompt],
            padding=True,
            return_tensors="pt"
        ).to(self.device)
        
        audio_values = self.model.generate(
            **inputs,
            max_new_tokens=int(duration * 50),
            guidance_scale=guidance_scale,
            audio_prompt=melody,
        )
        
        return audio_values.cpu().numpy()
    
    def save_audio(self, audio_array, output_path, sample_rate=32000):
        """保存音频文件"""
        # audio_array shape: [batch, channels, samples]
        if audio_array.ndim == 3:
            audio_array = audio_array[0]
        if audio_array.ndim == 2:
            # 取第一个通道或求平均
            audio_array = audio_array[0]
        
        # 归一化
        audio_array = audio_array / np.max(np.abs(audio_array))
        
        # 转换为16位整数
        audio_int16 = (audio_array * 32767).astype(np.int16)
        
        wavfile.write(output_path, sample_rate, audio_int16)
        print(f"音频已保存: {output_path}")
    
    def generate_variations(self, prompt, num_variations=4,
                            temperature_range=(0.8, 1.5)):
        """
        生成同一提示词的多个变体
        
        参数:
            prompt: 音乐描述
            num_variations: 变体数量
            temperature_range: 温度范围
        """
        variations = []
        temps = np.linspace(
            temperature_range[0],
            temperature_range[1],
            num_variations
        )
        
        for i, temp in enumerate(temps):
            print(f"生成变体 {i+1}/{num_variations}, 温度={temp:.2f}")
            audio = self.generate(
                prompt,
                duration=8,
                temperature=temp
            )
            variations.append(audio)
        
        return variations


# 使用示例
client = MusicGenClient(model_size="medium")

# 基础生成
audio = client.generate(
    "A cheerful acoustic guitar melody with gentle percussion, "
    "perfect for a morning coffee scene",
    duration=10,
    guidance_scale=3.5
)
client.save_audio(audio, "morning_coffee.wav")

# 生成多个变体
variations = client.generate_variations(
    "Lo-fi hip hop beat with jazzy piano chords and vinyl crackle",
    num_variations=4
)
for i, var in enumerate(variations):
    client.save_audio(var, f"lofi_variation_{i}.wav")

2.5 XTTS(Coqui开源语音克隆)

核心特性:

  • 零样本语音克隆(只需6秒参考音频)
  • 支持17种语言
  • 开源可本地部署
  • 支持流式合成
  • 情感和风格控制
# 安装: pip install TTS

from TTS.api import TTS
import torch

class XTTSClient:
    """XTTS语音合成与克隆客户端"""
    
    def __init__(self, model_name="tts_models/multilingual/multi-dataset/xtts_v2",
                 device="cuda"):
        """初始化XTTS"""
        print(f"加载XTTS模型: {model_name}")
        self.tts = TTS(model_name).to(device)
        self.device = device
        print("模型加载完成")
    
    def synthesize(self, text, output_path, speaker_wav=None,
                   language="zh"):
        """
        语音合成
        
        参数:
            text: 输入文本
            output_path: 输出路径
            speaker_wav: 参考音频路径(用于语音克隆)
            language: 语言代码
        """
        if speaker_wav:
            # 使用参考音频进行语音克隆
            self.tts.tts_to_file(
                text=text,
                speaker_wav=speaker_wav,
                language=language,
                file_path=output_path
            )
        else:
            # 使用默认声音
            self.tts.tts_to_file(
                text=text,
                language=language,
                file_path=output_path
            )
        
        print(f"音频已保存: {output_path}")
    
    def clone_and_speak(self, text, reference_audio, output_path,
                         language="zh"):
        """
        克隆声音并说话
        
        参数:
            text: 要说的文本
            reference_audio: 参考音频(至少6秒)
            output_path: 输出路径
            language: 语言
        """
        self.synthesize(text, output_path, reference_audio, language)
    
    def batch_clone(self, texts, reference_audio, output_dir,
                     language="zh"):
        """批量克隆合成"""
        import os
        os.makedirs(output_dir, exist_ok=True)
        
        for i, text in enumerate(texts):
            output_path = os.path.join(output_dir, f"cloned_{i:04d}.wav")
            try:
                self.clone_and_speak(
                    text, reference_audio, output_path, language
                )
            except Exception as e:
                print(f"生成失败 [{i}]: {e}")
    
    def multilingual_generate(self, text, output_dir, languages=None):
        """
        多语言生成同一文本
        
        参数:
            text: 输入文本
            output_dir: 输出目录
            languages: 语言列表
        """
        if languages is None:
            languages = ["en", "zh", "ja", "ko", "fr", "de", "es"]
        
        import os
        os.makedirs(output_dir, exist_ok=True)
        
        for lang in languages:
            output_path = os.path.join(output_dir, f"{lang}.wav")
            try:
                self.synthesize(text, output_path, language=lang)
                print(f"✓ {lang} 完成")
            except Exception as e:
                print(f"✗ {lang} 失败: {e}")


# 使用示例
xtts = XTTSClient()

# 基础合成
xtts.synthesize(
    "你好,欢迎使用AI语音合成系统!",
    output_path="hello.wav",
    language="zh"
)

# 语音克隆(需要参考音频)
xtts.clone_and_speak(
    "这是克隆后的声音,听起来是不是很像?",
    reference_audio="reference_voice.wav",
    output_path="cloned_output.wav",
    language="zh"
)

# 批量克隆
texts = [
    "第一条消息",
    "第二条消息",
    "第三条消息"
]
xtts.batch_clone(
    texts,
    reference_audio="reference_voice.wav",
    output_dir="cloned_batch",
    language="zh"
)

2.6 工具选择决策树

你需要AI音频生成?
├── 音乐生成?
│   ├── 需要带人声的完整歌曲?
│   │   ├── 最高品质 → Suno v3.5 / Udio
│   │   └── 开源免费 → 无直接替代(需组合多个模型)
│   ├── 纯音乐/背景音乐?
│   │   ├── 开源本地部署 → MusicGen
│   │   ├── 商业API → Suno/Udio
│   │   └── 精细控制 → Udio
│   └── 音乐编辑/混音?
│       └── Udio(支持扩展、混音、修复)
├── 语音合成(TTS)?
│   ├── 最高品质 → ElevenLabs
│   ├── 开源本地部署 → Bark / XTTS
│   ├── 多语言支持 → XTTS(17种语言)> Bark(13种)
│   └── 情感丰富 → Bark(笑声、叹气等)
├── 语音克隆?
│   ├── 零样本克隆 → XTTS(6秒参考音频)
│   ├── 高品质克隆 → ElevenLabs
│   └── 开源 → XTTS / Bark
└── 音频编辑?
    ├── 分离人声和伴奏 → Demucs
    ├── 音频修复 → Adobe Podcast / Descript
    └── 风格迁移 → AudioCraft

三、音乐生成原理

3.1 自回归Transformer方法

自回归方法是当前音乐生成的主流范式,以MusicGen和Suno为代表。其核心思想是将音乐表示为离散token序列,然后用Transformer逐步预测下一个token。

音乐的Token化表示:

import torch
import torch.nn as nn

class MusicTokenizer:
    """音乐Token化器 - 使用VQ-VAE将音频编码为离散token"""
    
    def __init__(self, codebook_size=2048, embedding_dim=128):
        self.codebook_size = codebook_size
        self.embedding_dim = embedding_dim
        
        # VQ-VAE组件
        self.encoder = nn.Sequential(
            nn.Conv1d(1, 64, kernel_size=7, padding=3),
            nn.ReLU(),
            nn.Conv1d(64, 128, kernel_size=5, stride=2, padding=2),
            nn.ReLU(),
            nn.Conv1d(128, 256, kernel_size=5, stride=2, padding=2),
            nn.ReLU(),
            nn.Conv1d(256, embedding_dim, kernel_size=3, padding=1),
        )
        
        self.decoder = nn.Sequential(
            nn.ConvTranspose1d(embedding_dim, 256, kernel_size=4, stride=2, padding=1),
            nn.ReLU(),
            nn.ConvTranspose1d(256, 128, kernel_size=4, stride=2, padding=1),
            nn.ReLU(),
            nn.ConvTranspose1d(128, 64, kernel_size=7, padding=3),
            nn.ReLU(),
            nn.Conv1d(64, 1, kernel_size=7, padding=3),
        )
        
        # 码本
        self.codebook = nn.Embedding(codebook_size, embedding_dim)
    
    def encode(self, audio):
        """
        将音频波形编码为token序列
        
        参数:
            audio: [batch, samples] 音频波形
        返回:
            tokens: [batch, seq_len] token序列
        """
        # 编码到连续空间
        features = self.encoder(audio.unsqueeze(1))  # [batch, embed_dim, seq_len]
        features = features.transpose(1, 2)  # [batch, seq_len, embed_dim]
        
        # 向量量化
        distances = torch.cdist(features, self.codebook.weight.unsqueeze(0))
        tokens = distances.argmin(dim=-1)  # [batch, seq_len]
        
        return tokens
    
    def decode(self, tokens):
        """
        将token序列解码为音频波形
        
        参数:
            tokens: [batch, seq_len] token序列
        返回:
            audio: [batch, samples] 音频波形
        """
        # 查找码本得到连续向量
        embeddings = self.codebook(tokens)  # [batch, seq_len, embed_dim]
        embeddings = embeddings.transpose(1, 2)  # [batch, embed_dim, seq_len]
        
        # 解码到波形
        audio = self.decoder(embeddings)  # [batch, 1, samples]
        
        return audio.squeeze(1)


class MusicTransformer(nn.Module):
    """自回归音乐生成Transformer"""
    
    def __init__(self, vocab_size=2048, d_model=1024, nhead=16,
                 num_layers=24, max_seq_len=2048):
        super().__init__()
        
        self.d_model = d_model
        self.vocab_size = vocab_size
        
        # Token嵌入
        self.token_embedding = nn.Embedding(vocab_size, d_model)
        self.position_embedding = nn.Embedding(max_seq_len, d_model)
        
        # Transformer解码器
        decoder_layer = nn.TransformerDecoderLayer(
            d_model=d_model,
            nhead=nhead,
            dim_feedforward=d_model * 4,
            dropout=0.1,
            batch_first=True
        )
        self.transformer = nn.TransformerDecoder(
            decoder_layer,
            num_layers=num_layers
        )
        
        # 输出头
        self.output_head = nn.Linear(d_model, vocab_size)
        
        # 条件嵌入(用于文本条件)
        self.condition_proj = nn.Linear(768, d_model)  # 假设文本编码维度为768
    
    def forward(self, tokens, condition=None, mask=None):
        """
        前向传播
        
        参数:
            tokens: [batch, seq_len] 输入token序列
            condition: [batch, cond_len, 768] 条件(如文本编码)
            mask: [seq_len, seq_len] 因果掩码
        """
        batch_size, seq_len = tokens.shape
        
        # 嵌入
        positions = torch.arange(seq_len, device=tokens.device).unsqueeze(0)
        x = self.token_embedding(tokens) + self.position_embedding(positions)
        
        # 处理条件
        if condition is not None:
            condition = self.condition_proj(condition)
        
        # 因果掩码(确保只能看到之前的token)
        if mask is None:
            mask = nn.Transformer.generate_square_subsequent_mask(seq_len)
            mask = mask.to(tokens.device)
        
        # Transformer处理
        if condition is not None:
            output = self.transformer(x, condition, tgt_mask=mask)
        else:
            # 自注意力模式
            output = self.transformer(x, x, tgt_mask=mask)
        
        # 预测下一个token
        logits = self.output_head(output)
        
        return logits
    
    @torch.no_grad()
    def generate(self, condition, max_length=1024, temperature=1.0,
                 top_k=50, top_p=0.9):
        """
        自回归生成音乐token序列
        
        参数:
            condition: 条件输入
            max_length: 最大生成长度
            temperature: 采样温度
            top_k: Top-K采样
            top_p: Top-P(核)采样
        """
        device = next(self.parameters()).device
        
        # 初始化序列(BOS token)
        tokens = torch.zeros(1, 1, dtype=torch.long, device=device)
        
        for _ in range(max_length):
            # 获取下一个token的logits
            logits = self.forward(tokens, condition)
            next_logits = logits[:, -1, :] / temperature
            
            # Top-K采样
            if top_k > 0:
                indices_to_remove = next_logits < torch.topk(next_logits, top_k)[0][:, -1, None]
                next_logits[indices_to_remove] = float('-inf')
            
            # Top-P采样
            if top_p < 1.0:
                sorted_logits, sorted_indices = torch.sort(next_logits, descending=True)
                cumulative_probs = torch.cumsum(torch.softmax(sorted_logits, dim=-1), dim=-1)
                sorted_indices_to_remove = cumulative_probs > top_p
                sorted_indices_to_remove[:, 1:] = sorted_indices_to_remove[:, :-1].clone()
                sorted_indices_to_remove[:, 0] = 0
                indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove)
                next_logits[indices_to_remove] = float('-inf')
            
            # 采样
            probs = torch.softmax(next_logits, dim=-1)
            next_token = torch.multinomial(probs, num_samples=1)
            
            # 添加到序列
            tokens = torch.cat([tokens, next_token], dim=1)
            
            # 检查是否生成了结束token
            if next_token.item() == 0:  # 假设0是EOS
                break
        
        return tokens

3.2 扩散模型方法

扩散模型在音乐生成中也取得了显著进展,代表工作包括AudioLDM和Riffusion。

import torch
import torch.nn as nn
import numpy as np

class AudioDiffusionModel(nn.Module):
    """音频扩散模型 - 在梅尔频谱图空间中工作"""
    
    def __init__(self, n_mels=128, time_steps=1000, hidden_dim=512):
        super().__init__()
        self.n_mels = n_mels
        self.time_steps = time_steps
        
        # 简化的U-Net用于频谱图去噪
        self.encoder = nn.ModuleList([
            self._make_encoder_block(1, 64),
            self._make_encoder_block(64, 128),
            self._make_encoder_block(128, 256),
        ])
        
        self.bottleneck = nn.Sequential(
            nn.Conv2d(256, 512, 3, padding=1),
            nn.GroupNorm(8, 512),
            nn.SiLU(),
            nn.Conv2d(512, 512, 3, padding=1),
            nn.GroupNorm(8, 512),
            nn.SiLU(),
        )
        
        self.decoder = nn.ModuleList([
            self._make_decoder_block(512, 256),
            self._make_decoder_block(256, 128),
            self._make_decoder_block(128, 64),
        ])
        
        self.output = nn.Conv2d(64, 1, 1)
        
        # 时间步嵌入
        self.time_embed = nn.Sequential(
            nn.Linear(1, hidden_dim),
            nn.SiLU(),
            nn.Linear(hidden_dim, hidden_dim)
        )
    
    def _make_encoder_block(self, in_ch, out_ch):
        return nn.Sequential(
            nn.Conv2d(in_ch, out_ch, 3, stride=2, padding=1),
            nn.GroupNorm(8, out_ch),
            nn.SiLU(),
            nn.Conv2d(out_ch, out_ch, 3, padding=1),
            nn.GroupNorm(8, out_ch),
            nn.SiLU(),
        )
    
    def _make_decoder_block(self, in_ch, out_ch):
        return nn.Sequential(
            nn.ConvTranspose2d(in_ch, out_ch, 4, stride=2, padding=1),
            nn.GroupNorm(8, out_ch),
            nn.SiLU(),
            nn.Conv2d(out_ch, out_ch, 3, padding=1),
            nn.GroupNorm(8, out_ch),
            nn.SiLU(),
        )
    
    def forward(self, x, t):
        """
        x: [batch, 1, n_mels, time] 噪声频谱图
        t: [batch] 时间步
        """
        # 时间步嵌入
        t_emb = self.time_embed(t.float().unsqueeze(-1))
        
        # 编码
        skips = []
        for enc in self.encoder:
            x = enc(x)
            skips.append(x)
        
        # 瓶颈
        x = self.bottleneck(x)
        
        # 解码
        for dec in self.decoder:
            x = dec(x)
            skip = skips.pop()
            x = x + skip  # 跳跃连接
        
        # 输出
        x = self.output(x)
        
        return x


class AudioDiffusionScheduler:
    """扩散过程调度器"""
    
    def __init__(self, num_timesteps=1000, beta_start=0.0001, beta_end=0.02):
        self.num_timesteps = num_timesteps
        
        # 线性噪声调度
        self.betas = torch.linspace(beta_start, beta_end, num_timesteps)
        self.alphas = 1 - self.betas
        self.alpha_cumprod = torch.cumprod(self.alphas, dim=0)
        self.sqrt_alpha_cumprod = torch.sqrt(self.alpha_cumprod)
        self.sqrt_one_minus_alpha_cumprod = torch.sqrt(1 - self.alpha_cumprod)
    
    def add_noise(self, x_0, t, noise=None):
        """向干净频谱图添加噪声"""
        if noise is None:
            noise = torch.randn_like(x_0)
        
        sqrt_alpha = self.sqrt_alpha_cumprod[t].reshape(-1, 1, 1, 1)
        sqrt_one_minus_alpha = self.sqrt_one_minus_alpha_cumprod[t].reshape(-1, 1, 1, 1)
        
        return sqrt_alpha * x_0 + sqrt_one_minus_alpha * noise, noise
    
    @torch.no_grad()
    def sample(self, model, shape, condition=None, num_inference_steps=50):
        """
        DDPM采样生成频谱图
        
        参数:
            model: 去噪模型
            shape: 输出形状 [batch, 1, n_mels, time]
            condition: 条件(如文本嵌入)
            num_inference_steps: 推理步数
        """
        device = next(model.parameters()).device
        
        # 从纯噪声开始
        x = torch.randn(shape, device=device)
        
        # 时间步序列
        timesteps = torch.linspace(
            self.num_timesteps - 1, 0, num_inference_steps
        ).long().to(device)
        
        for t in timesteps:
            t_batch = t.expand(shape[0])
            
            # 预测噪声
            predicted_noise = model(x, t_batch)
            
            # 去噪步骤
            alpha = self.alphas[t]
            alpha_cumprod = self.alpha_cumprod[t]
            
            # DDPM去噪公式
            x = (1 / torch.sqrt(alpha)) * (
                x - ((1 - alpha) / torch.sqrt(1 - alpha_cumprod)) * predicted_noise
            )
            
            # 添加噪声(除了最后一步)
            if t > 0:
                noise = torch.randn_like(x)
                x = x + torch.sqrt(self.betas[t]) * noise
        
        return x


class AudioLDMGenerator:
    """AudioLDM风格的音频生成器"""
    
    def __init__(self):
        self.model = AudioDiffusionModel()
        self.scheduler = AudioDiffusionScheduler()
    
    def generate_from_text(self, text_embedding, duration=10,
                            n_mels=128, sr=16000):
        """
        从文本嵌入生成音频
        
        参数:
            text_embedding: 文本的CLAP嵌入 [batch, embed_dim]
            duration: 时长(秒)
            n_mels: 梅尔频谱bin数
            sr: 采样率
        """
        # 计算频谱图的时间维度
        time_steps = int(duration * sr / 512)  # hop_length=512
        
        # 生成频谱图
        shape = (text_embedding.shape[0], 1, n_mels, time_steps)
        mel_spectrogram = self.scheduler.sample(
            self.model, shape, condition=text_embedding
        )
        
        # 从梅尔频谱图恢复波形
        audio = self.mel_to_audio(mel_spectrogram)
        
        return audio
    
    def mel_to_audio(self, mel_spec):
        """梅尔频谱图转波形(使用Griffin-Lim算法简化版)"""
        # 实际应用中应使用HiFi-GAN等神经声码器
        import librosa
        
        mel = mel_spec.squeeze().cpu().numpy()
        
        # 反梅尔变换
        audio = librosa.feature.inverse.mel_to_audio(
            mel, sr=16000, n_fft=1024, hop_length=512
        )
        
        return audio

3.3 多尺度生成策略

现代音乐生成系统通常采用多尺度策略来处理不同层次的音乐结构:

class MultiScaleMusicGenerator:
    """
    多尺度音乐生成器
    - 小尺度:音符/音素级别(~50ms)
    - 中尺度:乐句/小节级别(~2s)
    - 大尺度:段落/曲式级别(~30s)
    """
    
    def __init__(self):
        # 三个不同尺度的生成器
        self.note_generator = NoteLevelGenerator()
        self.phrase_generator = PhraseLevelGenerator()
        self.section_generator = SectionLevelGenerator()
    
    def generate(self, structure, style, duration=60):
        """
        从结构描述生成完整音乐
        
        structure: {
            "intro": {"duration": 8, "energy": "low"},
            "verse1": {"duration": 16, "energy": "medium"},
            "chorus": {"duration": 16, "energy": "high"},
            "verse2": {"duration": 16, "energy": "medium"},
            "outro": {"duration": 8, "energy": "low"}
        }
        """
        sections = []
        
        for section_name, section_info in structure.items():
            print(f"生成段落: {section_name}")
            
            # 大尺度:生成段落结构
            section_structure = self.section_generator.generate(
                style=style,
                energy=section_info["energy"],
                duration=section_info["duration"]
            )
            
            # 中尺度:生成乐句
            phrases = []
            for phrase_info in section_structure:
                phrase = self.phrase_generator.generate(
                    style=style,
                    chord_progression=phrase_info["chords"],
                    rhythm_pattern=phrase_info["rhythm"]
                )
                phrases.append(phrase)
            
            # 小尺度:生成音符
            notes = []
            for phrase in phrases:
                note_seq = self.note_generator.generate(
                    phrase=phrase,
                    velocity=section_info.get("velocity", 80)
                )
                notes.append(note_seq)
            
            sections.append({
                "name": section_name,
                "notes": notes,
                "duration": section_info["duration"]
            })
        
        return sections


class NoteLevelGenerator:
    """音符级别生成器"""
    
    def generate(self, phrase, velocity=80):
        """
        生成具体的音符序列
        
        phrase: 乐句信息
        velocity: 力度 (0-127)
        """
        notes = []
        
        # 基于乐句的和弦和节奏生成音符
        for beat in phrase["beats"]:
            note = {
                "pitch": beat["pitch"],
                "velocity": velocity + beat.get("velocity_offset", 0),
                "duration": beat["duration"],
                "start": beat["start"]
            }
            notes.append(note)
        
        return notes


class PhraseLevelGenerator:
    """乐句级别生成器"""
    
    # 常用和弦进行
    CHORD_PROGRESSIONS = {
        "pop_major": ["I", "V", "vi", "IV"],
        "pop_minor": ["i", "VI", "III", "VII"],
        "jazz_ii_v_i": ["ii7", "V7", "Imaj7"],
        "blues": ["I7", "I7", "I7", "I7", "IV7", "IV7", "I7", "I7", "V7", "IV7", "I7", "V7"],
        "electronic": ["i", "VI", "III", "VII"],
    }
    
    def generate(self, style, chord_progression=None, rhythm_pattern=None):
        """生成乐句"""
        if chord_progression is None:
            chord_progression = self.CHORD_PROGRESSIONS.get(
                style, self.CHORD_PROGRESSIONS["pop_major"]
            )
        
        beats = []
        current_beat = 0
        
        for chord in chord_progression:
            # 基于和弦生成伴奏音型
            chord_notes = self._chord_to_notes(chord)
            
            for note in chord_notes:
                beats.append({
                    "pitch": note,
                    "start": current_beat,
                    "duration": 0.5,
                    "velocity_offset": 0
                })
            
            current_beat += 1.0
        
        return {"beats": beats, "chords": chord_progression}
    
    def _chord_to_notes(self, chord):
        """将和弦符号转换为MIDI音符"""
        # 简化的和弦到音符映射
        base_notes = {
            "I": [60, 64, 67],      # C大三和弦
            "ii": [62, 65, 69],      # Dm
            "iii": [64, 67, 71],     # Em
            "IV": [65, 69, 72],      # F
            "V": [67, 71, 74],       # G
            "vi": [69, 72, 76],      # Am
            "i": [60, 63, 67],       # Cm
            "VI": [69, 72, 76],      # Ab
            "III": [64, 67, 71],     # Eb
            "VII": [70, 74, 77],     # Bb
        }
        
        # 去掉数字后缀
        clean_chord = chord.rstrip("0123456789majdim")
        return base_notes.get(clean_chord, [60, 64, 67])


class SectionLevelGenerator:
    """段落级别生成器"""
    
    def generate(self, style, energy, duration):
        """
        生成段落结构
        
        参数:
            style: 音乐风格
            energy: 能量级别 (low/medium/high)
            duration: 段落时长(秒)
        """
        # 根据能量级别决定乐句数量和特征
        beats_per_second = {"low": 1, "medium": 2, "high": 4}
        bps = beats_per_second.get(energy, 2)
        
        num_phrases = max(1, int(duration / 4))  # 每4秒一个乐句
        
        phrases = []
        for i in range(num_phrases):
            phrase = {
                "chords": self._select_chords(style, energy, i, num_phrases),
                "rhythm": self._select_rhythm(style, energy),
                "energy": energy
            }
            phrases.append(phrase)
        
        return phrases
    
    def _select_chords(self, style, energy, position, total):
        """选择和弦进行"""
        if energy == "high":
            return ["I", "V", "vi", "IV"]
        elif energy == "medium":
            return ["I", "IV", "V", "IV"]
        else:
            return ["I", "I", "IV", "IV"]
    
    def _select_rhythm(self, style, energy):
        """选择节奏型"""
        rhythms = {
            "low": "whole_notes",
            "medium": "quarter_notes",
            "high": "eighth_notes"
        }
        return rhythms.get(energy, "quarter_notes")

四、歌词到音乐生成

4.1 Suno风格的歌词生成Pipeline

class LyricsToMusic:
    """歌词到音乐的完整Pipeline"""
    
    # 音乐风格模板
    STYLE_TEMPLATES = {
        "流行": {
            "tempo": "medium (100-120 BPM)",
            "instruments": "acoustic guitar, piano, light drums, bass",
            "vocal_style": "clear, melodic, emotional",
            "structure": "verse-chorus-verse-chorus-bridge-chorus"
        },
        "摇滚": {
            "tempo": "fast (120-150 BPM)",
            "instruments": "electric guitar, bass, heavy drums, synth",
            "vocal_style": "powerful, raspy, energetic",
            "structure": "intro-verse-chorus-verse-chorus-solo-chorus"
        },
        "电子": {
            "tempo": "variable (120-140 BPM)",
            "instruments": "synthesizers, drum machine, bass synth",
            "vocal_style": "processed, auto-tuned, ethereal",
            "structure": "intro-buildup-drop-breakdown-drop-outro"
        },
        "说唱": {
            "tempo": "medium-fast (90-110 BPM)",
            "instruments": "808 drums, trap hi-hats, synth bass",
            "vocal_style": "rhythmic, spoken, aggressive",
            "structure": "verse-hook-verse-hook-bridge-hook"
        },
        "民谣": {
            "tempo": "slow (80-100 BPM)",
            "instruments": "acoustic guitar, harmonica, light percussion",
            "vocal_style": "gentle, storytelling, intimate",
            "structure": "verse-verse-chorus-verse-chorus"
        },
        "R&B": {
            "tempo": "medium (85-100 BPM)",
            "instruments": "keys, bass, smooth drums, strings",
            "vocal_style": "soulful, smooth, melismatic",
            "structure": "verse-prechorus-chorus-verse-prechorus-chorus-bridge-chorus"
        }
    }
    
    def __init__(self, suno_client=None):
        self.suno_client = suno_client
    
    def generate_from_lyrics(self, lyrics, style="流行",
                              title=None, language="zh"):
        """
        从歌词生成完整歌曲
        
        参数:
            lyrics: 歌词文本
            style: 音乐风格
            title: 歌曲标题
            language: 语言
        """
        # 分析歌词结构
        structure = self._parse_lyrics_structure(lyrics)
        
        # 构建音乐描述
        style_info = self.STYLE_TEMPLATES.get(style, self.STYLE_TEMPLATES["流行"])
        
        prompt = self._build_music_prompt(style_info, structure, language)
        
        # 调用生成API
        if self.suno_client:
            result = self.suno_client.generate_music(
                prompt=prompt,
                lyrics=lyrics,
                style=f"{style}, {style_info['instruments']}",
                title=title or "AI Generated Song",
                instrumental=False
            )
            return result
        
        return {
            "prompt": prompt,
            "lyrics": lyrics,
            "style": style_info
        }
    
    def _parse_lyrics_structure(self, lyrics):
        """
        解析歌词结构
        
        识别 [Verse], [Chorus], [Bridge] 等标记
        """
        import re
        
        structure = {
            "sections": [],
            "total_lines": 0,
            "has_chorus": False,
            "has_bridge": False
        }
        
        current_section = None
        lines = lyrics.split("\n")
        
        for line in lines:
            line = line.strip()
            
            # 检测段落标记
            section_match = re.match(r'\[(.*?)\]', line)
            if section_match:
                section_name = section_match.group(1).lower()
                current_section = section_name
                
                if "chorus" in section_name:
                    structure["has_chorus"] = True
                elif "bridge" in section_name:
                    structure["has_bridge"] = True
                
                structure["sections"].append({
                    "type": section_name,
                    "lines": []
                })
            elif line and current_section:
                structure["sections"][-1]["lines"].append(line)
                structure["total_lines"] += 1
        
        return structure
    
    def _build_music_prompt(self, style_info, structure, language):
        """构建音乐生成提示词"""
        parts = [
            f"A {style_info['tempo']} song",
            f"featuring {style_info['instruments']}",
            f"with {style_info['vocal_style']} vocals",
            f"in {language} language"
        ]
        
        if structure["has_chorus"]:
            parts.append("with a catchy, memorable chorus")
        if structure["has_bridge"]:
            parts.append("with a contrasting bridge section")
        
        return ", ".join(parts)
    
    def generate_instrumental(self, description, style="电子",
                               duration=60):
        """
        生成纯音乐(无人声)
        
        参数:
            description: 音乐描述
            style: 风格
            duration: 时长
        """
        style_info = self.STYLE_TEMPLATES.get(style, self.STYLE_TEMPLATES["电子"])
        
        prompt = (
            f"{description}, "
            f"instrumental, no vocals, "
            f"{style_info['tempo']}, "
            f"featuring {style_info['instruments']}"
        )
        
        if self.suno_client:
            return self.suno_client.generate_music(
                prompt=prompt,
                instrumental=True
            )
        
        return {"prompt": prompt}


# 使用示例
client = SunoClient("your_api_key")
lyrics_gen = LyricsToMusic(client)

# 歌词示例
lyrics = """[Verse 1]
城市的灯光照亮了夜空
我站在天桥上看着车流
每一盏灯都是一个故事
每辆车都载着一个梦想

[Chorus]
我们在城市的海洋里航行
寻找着属于自己的方向
不管风雨多么猛烈
我们都要勇敢地前行

[Verse 2]
清晨的地铁里人潮涌动
每个人都在追逐着什么
也许答案就在身边
只是我们还没有发现

[Chorus]
我们在城市的海洋里航行
寻找着属于自己的方向
不管风雨多么猛烈
我们都要勇敢地前行

[Bridge]
停下脚步看看天空
星星一直在那里
只是我们太忙碌
忘了抬头仰望

[Chorus]
我们在城市的海洋里航行
寻找着属于自己的方向
不管风雨多么猛烈
我们都要勇敢地前行"""

result = lyrics_gen.generate_from_lyrics(
    lyrics=lyrics,
    style="流行",
    title="城市航行",
    language="zh"
)
print(json.dumps(result, indent=2, ensure_ascii=False))

4.2 歌词自动创作

class LyricGenerator:
    """AI辅助歌词创作"""
    
    # 押韵词库
    RHYME_DICT = {
        "ang": ["光", "方", "望", "想", "唱", "茫", "浪", "长"],
        "ing": ["星", "明", "情", "听", "行", "晴", "清", "灵"],
        "ong": ["中", "空", "梦", "风", "红", "同", "动", "虹"],
        "ai": ["海", "白", "来", "在", "开", "爱", "彩", "待"],
        "an": ["山", "天", "间", "蓝", "安", "远", "前", "年"],
        "en": ["人", "真", "尘", "分", "春", "心", "新", "深"],
    }
    
    # 歌词模板
    TEMPLATES = {
        "verse": {
            "structure": "AABB",
            "lines": 4,
            "description": "主歌,讲述故事或描述场景"
        },
        "chorus": {
            "structure": "ABAB",
            "lines": 4,
            "description": "副歌,表达核心情感和主题"
        },
        "bridge": {
            "structure": "ABCB",
            "lines": 4,
            "description": "桥段,情感转折或升华"
        }
    }
    
    def generate_verse(self, theme, mood="positive", rhyme_group="ang"):
        """
        生成主歌歌词
        
        参数:
            theme: 主题
            mood: 情绪 (positive/negative/neutral)
            rhyme_group: 押韵组
        """
        rhyme_words = self.RHYME_DICT.get(rhyme_group, self.RHYME_DICT["ang"])
        
        # 基于模板生成(实际应使用LLM)
        verse = {
            "type": "verse",
            "lines": [],
            "rhyme_scheme": "AABB"
        }
        
        return verse
    
    def generate_chorus(self, title, theme, energy="high"):
        """
        生成副歌歌词
        
        参数:
            title: 歌曲标题
            theme: 主题
            energy: 能量级别
        """
        chorus = {
            "type": "chorus",
            "title_hook": title,
            "lines": [],
            "repeat": True  # 副歌通常重复
        }
        
        return chorus
    
    def complete_song(self, theme, style="流行", language="zh"):
        """
        生成完整歌曲结构
        
        参数:
            theme: 主题
            style: 风格
            language: 语言
        """
        # 标准歌曲结构
        song_structure = [
            {"type": "verse", "label": "Verse 1"},
            {"type": "chorus", "label": "Chorus"},
            {"type": "verse", "label": "Verse 2"},
            {"type": "chorus", "label": "Chorus"},
            {"type": "bridge", "label": "Bridge"},
            {"type": "chorus", "label": "Final Chorus"},
        ]
        
        song = {
            "theme": theme,
            "style": style,
            "language": language,
            "structure": song_structure,
            "lyrics": ""
        }
        
        return song

五、语音合成TTS技术

5.1 传统TTS方法回顾

"""
传统TTS方法对比
"""

# 1. 拼接合成(Concatenative Synthesis)
class ConcatenativeTTS:
    """
    拼接合成:从预录音频库中选择并拼接语音单元
    优点:自然度高(使用真实录音)
    缺点:需要大量录音数据,拼接痕迹
    """
    
    def __init__(self, audio_db):
        self.audio_db = audio_db  # 预录音频数据库
        self.unit_index = {}  # 音素到音频片段的索引
    
    def build_index(self):
        """构建音素索引"""
        for audio_path, metadata in self.audio_db.items():
            for phoneme, start, end in metadata["phonemes"]:
                if phoneme not in self.unit_index:
                    self.unit_index[phoneme] = []
                self.unit_index[phoneme].append({
                    "path": audio_path,
                    "start": start,
                    "end": end
                })
    
    def synthesize(self, phoneme_sequence):
        """合成语音"""
        audio_segments = []
        
        for phoneme in phoneme_sequence:
            if phoneme in self.unit_index:
                # 选择最佳匹配的音频片段
                segment = self._select_best_segment(phoneme, audio_segments)
                audio_segments.append(segment)
        
        # 拼接并平滑
        return self._concatenate_and_smooth(audio_segments)
    
    def _select_best_segment(self, phoneme, context):
        """选择最佳匹配的音频片段(考虑上下文)"""
        candidates = self.unit_index[phoneme]
        
        if not context:
            return candidates[0]
        
        # 简化的选择策略:选择与上一个片段音高最接近的
        return candidates[0]
    
    def _concatenate_and_smooth(self, segments):
        """拼接并平滑过渡"""
        import numpy as np
        
        if not segments:
            return np.array([])
        
        # 简单拼接,实际需要交叉淡化
        return np.concatenate(segments)


# 2. 参数合成(Parametric Synthesis)
class ParametricTTS:
    """
    参数合成:使用统计模型生成语音参数
    优点:灵活,可调整语速、音高等
    缺点:音质不如拼接合成
    """
    
    def __init__(self):
        # 声学模型(预测梅尔频谱图)
        self.acoustic_model = None
        # 声码器(从频谱图生成波形)
        self.vocoder = None
    
    def synthesize(self, text):
        """
        参数合成流程:
        1. 文本 → 语言学特征
        2. 语言学特征 → 声学参数(梅尔频谱图)
        3. 声学参数 → 波形
        """
        # 步骤1:文本分析
        linguistic_features = self._text_analysis(text)
        
        # 步骤2:声学模型预测
        mel_spectrogram = self.acoustic_model(linguistic_features)
        
        # 步骤3:声码器生成波形
        waveform = self.vocoder(mel_spectrogram)
        
        return waveform
    
    def _text_analysis(self, text):
        """文本分析:提取语言学特征"""
        # 包括:音素序列、韵律标记、词性标注等
        features = {
            "phonemes": self._grapheme_to_phoneme(text),
            "prosody": self._extract_prosody(text),
            "duration": self._predict_duration(text)
        }
        return features
    
    def _grapheme_to_phoneme(self, text):
        """字素到音素转换"""
        # 简化版,实际需要G2P模型
        return list(text)
    
    def _extract_prosody(self, text):
        """提取韵律信息"""
        return {"pitch": 1.0, "energy": 1.0, "rate": 1.0}
    
    def _predict_duration(self, text):
        """预测音素时长"""
        return [0.1] * len(text)

5.2 神经网络TTS

import torch
import torch.nn as nn

class Tacotron2(nn.Module):
    """
    Tacotron 2 端到端TTS模型
    文本 → 梅尔频谱图
    """
    
    def __init__(self, vocab_size, embedding_dim=512, encoder_dim=512,
                 decoder_dim=1024, n_mels=80):
        super().__init__()
        
        # 文本编码器
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
        self.encoder = nn.LSTM(
            embedding_dim, encoder_dim // 2,
            num_layers=3, bidirectional=True, batch_first=True
        )
        
        # 注意力机制
        self.attention = LocationSensitiveAttention(
            encoder_dim, decoder_dim, n_filters=32, kernel_size=31
        )
        
        # 解码器
        self.decoder = nn.LSTMCell(
            encoder_dim + n_mels, decoder_dim
        )
        self.mel_projection = nn.Linear(decoder_dim, n_mels)
        self.stop_projection = nn.Linear(decoder_dim, 1)
        
        # 后处理网络(梅尔频谱图后处理)
        self.postnet = nn.Sequential(
            nn.Conv1d(n_mels, 512, 5, padding=2),
            nn.BatchNorm1d(512),
            nn.Tanh(),
            nn.Dropout(0.5),
            nn.Conv1d(512, 512, 5, padding=2),
            nn.BatchNorm1d(512),
            nn.Tanh(),
            nn.Dropout(0.5),
            nn.Conv1d(512, n_mels, 5, padding=2),
        )
    
    def forward(self, text, max_len=1000):
        """
        前向传播
        
        参数:
            text: [batch, text_len] 文本token序列
            max_len: 最大解码长度
        """
        batch_size = text.shape[0]
        
        # 文本编码
        embedded = self.embedding(text)
        encoder_outputs, _ = self.encoder(embedded)
        
        # 初始化解码器状态
        decoder_hidden = torch.zeros(batch_size, 1024, device=text.device)
        decoder_cell = torch.zeros(batch_size, 1024, device=text.device)
        context = torch.zeros(batch_size, encoder_outputs.shape[-1], device=text.device)
        
        # 自回归解码
        mel_outputs = []
        stop_tokens = []
        
        # 初始输入(全零帧)
        decoder_input = torch.zeros(batch_size, 80, device=text.device)
        
        for step in range(max_len):
            # 解码器输入 = 上一帧梅尔频谱 + 上下文
            decoder_input_cat = torch.cat([decoder_input, context], dim=-1)
            
            # LSTM解码
            decoder_hidden, decoder_cell = self.decoder(
                decoder_input_cat, (decoder_hidden, decoder_cell)
            )
            
            # 注意力
            context, attention_weights = self.attention(
                decoder_hidden, encoder_outputs
            )
            
            # 预测梅尔频谱帧
            mel_frame = self.mel_projection(
                torch.cat([decoder_hidden, context], dim=-1)
            )
            
            # 预测停止标记
            stop_token = self.stop_projection(decoder_hidden)
            
            mel_outputs.append(mel_frame)
            stop_tokens.append(stop_token)
            
            # 更新输入(Teacher Forcing时使用真实帧)
            decoder_input = mel_frame
            
            # 检查是否应该停止
            if torch.sigmoid(stop_token).mean() > 0.5:
                break
        
        # 堆叠输出
        mel_outputs = torch.stack(mel_outputs, dim=1)
        stop_tokens = torch.stack(stop_tokens, dim=1)
        
        # 后处理
        postnet_output = self.postnet(mel_outputs.transpose(1, 2)).transpose(1, 2)
        mel_postnet = mel_outputs + postnet_output
        
        return {
            "mel": mel_outputs,
            "mel_postnet": mel_postnet,
            "stop_tokens": stop_tokens,
            "attention_weights": attention_weights
        }


class LocationSensitiveAttention(nn.Module):
    """位置敏感注意力机制"""
    
    def __init__(self, encoder_dim, decoder_dim, n_filters=32, kernel_size=31):
        super().__init__()
        
        self.query_projection = nn.Linear(decoder_dim, decoder_dim)
        self.memory_projection = nn.Linear(encoder_dim, decoder_dim)
        
        # 位置特征卷积
        self.location_conv = nn.Conv1d(
            2, n_filters, kernel_size, padding=kernel_size // 2
        )
        self.location_projection = nn.Linear(n_filters, decoder_dim)
        
        self.v = nn.Linear(decoder_dim, 1)
        self.score_mask = -float("inf")
    
    def forward(self, query, memory, attention_weights_prev=None):
        """
        query: [batch, decoder_dim] 解码器状态
        memory: [batch, encoder_len, encoder_dim] 编码器输出
        attention_weights_prev: [batch, encoder_len] 上一步的注意力权重
        """
        batch_size, encoder_len, _ = memory.shape
        
        # 查询投影
        query_proj = self.query_projection(query).unsqueeze(1)  # [batch, 1, dim]
        
        # 计算位置特征
        if attention_weights_prev is None:
            attention_weights_prev = torch.zeros(batch_size, encoder_len, device=query.device)
        
        location_features = self.location_conv(
            attention_weights_prev.unsqueeze(1)
        ).transpose(1, 2)
        location_proj = self.location_projection(location_features)
        
        # 计算注意力分数
        memory_proj = self.memory_projection(memory)
        
        scores = self.v(torch.tanh(
            query_proj + memory_proj + location_proj
        )).squeeze(-1)
        
        # Softmax
        attention_weights = torch.softmax(scores, dim=-1)
        
        # 加权求和
        context = torch.bmm(attention_weights.unsqueeze(1), memory).squeeze(1)
        
        return context, attention_weights


class HiFiGANVocoder(nn.Module):
    """
    HiFi-GAN声码器
    梅尔频谱图 → 波形
    """
    
    def __init__(self, n_mels=80, upsample_rates=[8, 8, 2, 2],
                 upsample_initial_channel=512):
        super().__init__()
        
        # 上采样网络
        self.pre_conv = nn.Conv1d(n_mels, upsample_initial_channel, 7, padding=3)
        
        self.ups = nn.ModuleList()
        self.res_blocks = nn.ModuleList()
        
        current_channel = upsample_initial_channel
        for i, rate in enumerate(upsample_rates):
            upsample_channel = current_channel // 2
            self.ups.append(
                nn.ConvTranspose1d(
                    current_channel, upsample_channel,
                    rate * 2, stride=rate, padding=rate // 2
                )
            )
            self.res_blocks.append(
                ResBlock(upsample_channel, kernel_sizes=[3, 7, 11])
            )
            current_channel = upsample_channel
        
        # 输出层
        self.post_conv = nn.Sequential(
            nn.Conv1d(current_channel, 1, 7, padding=3),
            nn.Tanh()
        )
    
    def forward(self, mel):
        """
        mel: [batch, n_mels, time] 梅尔频谱图
        输出: [batch, samples] 波形
        """
        x = self.pre_conv(mel)
        
        for up, res_block in zip(self.ups, self.res_blocks):
            x = up(x)
            x = res_block(x)
        
        x = self.post_conv(x)
        
        return x.squeeze(1)


class ResBlock(nn.Module):
    """残差块"""
    
    def __init__(self, channels, kernel_sizes=[3, 7, 11]):
        super().__init__()
        
        self.convs = nn.ModuleList()
        for k in kernel_sizes:
            self.convs.append(nn.Sequential(
                nn.LeakyReLU(0.1),
                nn.Conv1d(channels, channels, k, padding=k // 2),
                nn.LeakyReLU(0.1),
                nn.Conv1d(channels, channels, k, padding=k // 2),
            ))
    
    def forward(self, x):
        for conv in self.convs:
            x = x + conv(x)
        return x

5.3 零样本语音克隆技术

class ZeroShotVoiceCloner:
    """
    零样本语音克隆
    使用参考音频的说话人嵌入来控制合成语音的音色
    """
    
    def __init__(self, encoder_model, tts_model, vocoder):
        """
        encoder_model: 说话人编码器(提取音色特征)
        tts_model: 条件TTS模型
        vocoder: 声码器
        """
        self.encoder = encoder_model
        self.tts = tts_model
        self.vocoder = vocoder
    
    def extract_speaker_embedding(self, reference_audio):
        """
        从参考音频提取说话人嵌入
        
        参数:
            reference_audio: 参考音频波形 [samples] 或 [1, samples]
        """
        if reference_audio.dim() == 1:
            reference_audio = reference_audio.unsqueeze(0)
        
        # 提取说话人嵌入
        with torch.no_grad():
            speaker_embedding = self.encoder(reference_audio)
        
        return speaker_embedding
    
    def clone_and_speak(self, text, reference_audio, language="zh"):
        """
        克隆声音并合成语音
        
        参数:
            text: 要合成的文本
            reference_audio: 参考音频(用于提取音色)
            language: 语言
        """
        # 步骤1:提取说话人嵌入
        speaker_emb = self.extract_speaker_embedding(reference_audio)
        
        # 步骤2:条件TTS生成梅尔频谱图
        mel_output = self.tts(text, speaker_emb, language)
        
        # 步骤3:声码器生成波形
        waveform = self.vocoder(mel_output)
        
        return waveform
    
    def interpolate_voices(self, text, ref_audio_1, ref_audio_2,
                            alpha=0.5):
        """
        混合两个说话人的音色
        
        参数:
            text: 文本
            ref_audio_1: 参考音频1
            ref_audio_2: 参考音频2
            alpha: 混合比例 (0=完全使用音频1, 1=完全使用音频2)
        """
        emb1 = self.extract_speaker_embedding(ref_audio_1)
        emb2 = self.extract_speaker_embedding(ref_audio_2)
        
        # 线性插值
        mixed_emb = (1 - alpha) * emb1 + alpha * emb2
        
        # 使用混合嵌入生成语音
        mel_output = self.tts(text, mixed_emb)
        waveform = self.vocoder(mel_output)
        
        return waveform


class SpeakerEncoder(nn.Module):
    """
    说话人编码器
    使用GE2E(Generalized End-to-End)损失训练
    """
    
    def __init__(self, input_dim=40, hidden_dim=256, embedding_dim=256):
        super().__init__()
        
        # 3层LSTM
        self.lstm = nn.LSTM(
            input_dim, hidden_dim,
            num_layers=3, batch_first=True
        )
        
        # 投影层
        self.projection = nn.Linear(hidden_dim, embedding_dim)
        
        # 归一化
        self.relu = nn.ReLU()
    
    def forward(self, mels):
        """
        mels: [batch, time, n_mels] 梅尔频谱图
        输出: [batch, embedding_dim] 说话人嵌入
        """
        # LSTM处理
        lstm_out, (hidden, _) = self.lstm(mels)
        
        # 取最后一层的最后一个时间步
        embedding = self.projection(hidden[-1])
        
        # L2归一化
        embedding = torch.nn.functional.normalize(embedding, p=2, dim=-1)
        
        return embedding


# 语音克隆使用示例
def voice_clone_example():
    """语音克隆示例"""
    import torchaudio
    
    # 加载参考音频
    reference_waveform, sample_rate = torchaudio.load("reference_voice.wav")
    
    # 重采样到16kHz
    if sample_rate != 16000:
        resampler = torchaudio.transforms.Resample(sample_rate, 16000)
        reference_waveform = resampler(reference_waveform)
    
    # 使用XTTS进行克隆(简化示例)
    from TTS.api import TTS
    
    tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2")
    
    # 克隆生成
    tts.tts_to_file(
        text="这是克隆后的声音,听起来和参考音频很像。",
        speaker_wav="reference_voice.wav",
        language="zh",
        file_path="cloned_output.wav"
    )
    
    print("语音克隆完成: cloned_output.wav")

六、音频风格迁移

6.1 音乐风格迁移

class MusicStyleTransfer:
    """
    音乐风格迁移
    将一首歌的风格转移到另一首歌上
    """
    
    def __init__(self):
        # 风格编码器
        self.style_encoder = StyleEncoder()
        # 内容编码器
        self.content_encoder = ContentEncoder()
        # 解码器
        self.decoder = MusicDecoder()
    
    def transfer(self, content_audio, style_audio,
                  content_weight=1.0, style_weight=0.5):
        """
        音乐风格迁移
        
        参数:
            content_audio: 内容音频(要保留旋律)
            style_audio: 风格音频(要迁移的风格)
            content_weight: 内容保持权重
            style_weight: 风格迁移权重
        """
        # 提取内容特征
        content_features = self.content_encoder(content_audio)
        
        # 提取风格特征
        style_features = self.style_encoder(style_audio)
        
        # 融合特征
        blended = content_weight * content_features + style_weight * style_features
        
        # 解码生成新音频
        output_audio = self.decoder(blended)
        
        return output_audio
    
    def transfer_with_grl(self, content_audio, style_audio):
        """
        使用梯度反转层的风格迁移
        """
        # 提取梅尔频谱图
        content_mel = self._audio_to_mel(content_audio)
        style_mel = self._audio_to_mel(style_audio)
        
        # AdaIN风格迁移
        output_mel = self._adain_transfer(content_mel, style_mel)
        
        # 声码器生成波形
        output_audio = self._mel_to_audio(output_mel)
        
        return output_audio
    
    def _adain_transfer(self, content, style):
        """
        Adaptive Instance Normalization风格迁移
        """
        # 计算内容和风格的统计量
        content_mean = content.mean(dim=-1, keepdim=True)
        content_std = content.std(dim=-1, keepdim=True) + 1e-6
        
        style_mean = style.mean(dim=-1, keepdim=True)
        style_std = style.std(dim=-1, keepdim=True) + 1e-6
        
        # 归一化内容
        normalized = (content - content_mean) / content_std
        
        # 应用风格统计量
        transferred = normalized * style_std + style_mean
        
        return transferred
    
    def _audio_to_mel(self, audio, sr=16000, n_mels=80):
        """音频转梅尔频谱图"""
        import librosa
        mel = librosa.feature.melspectrogram(
            y=audio, sr=sr, n_mels=n_mels, n_fft=1024, hop_length=256
        )
        return torch.from_numpy(mel)
    
    def _mel_to_audio(self, mel, sr=16000):
        """梅尔频谱图转音频"""
        import librosa
        audio = librosa.feature.inverse.mel_to_audio(
            mel.numpy(), sr=sr, n_fft=1024, hop_length=256
        )
        return audio


class StyleEncoder(nn.Module):
    """风格编码器 - 提取音乐风格特征"""
    
    def __init__(self, n_mels=80, style_dim=128):
        super().__init__()
        
        self.conv_layers = nn.Sequential(
            nn.Conv1d(n_mels, 64, 3, padding=1),
            nn.ReLU(),
            nn.Conv1d(64, 128, 3, padding=1),
            nn.ReLU(),
            nn.AdaptiveAvgPool1d(1)
        )
        
        self.fc = nn.Linear(128, style_dim)
    
    def forward(self, mel):
        """
        mel: [batch, n_mels, time]
        输出: [batch, style_dim]
        """
        x = self.conv_layers(mel)
        x = x.squeeze(-1)
        return self.fc(x)


class ContentEncoder(nn.Module):
    """内容编码器 - 提取旋律和节奏特征"""
    
    def __init__(self, n_mels=80, content_dim=256):
        super().__init__()
        
        self.conv_layers = nn.Sequential(
            nn.Conv1d(n_mels, 128, 3, padding=1),
            nn.ReLU(),
            nn.Conv1d(128, 256, 3, padding=1),
            nn.ReLU(),
        )
        
        self.fc = nn.Linear(256, content_dim)
    
    def forward(self, mel):
        x = self.conv_layers(mel)
        x = x.transpose(1, 2)
        return self.fc(x)


class MusicDecoder(nn.Module):
    """音乐解码器"""
    
    def __init__(self, content_dim=256, n_mels=80):
        super().__init__()
        
        self.fc = nn.Linear(content_dim, 256)
        self.conv_layers = nn.Sequential(
            nn.ConvTranspose1d(256, 128, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose1d(128, n_mels, 3, padding=1),
        )
    
    def forward(self, content_features):
        x = self.fc(content_features)
        x = x.transpose(1, 2)
        return self.conv_layers(x)

6.2 语音风格迁移

class VoiceStyleTransfer:
    """
    语音风格迁移
    将一个人的说话风格(语调、节奏)应用到另一个人的音色上
    """
    
    def __init__(self):
        self.pitch_extractor = PitchExtractor()
        self.duration_model = DurationModel()
    
    def transfer_style(self, source_audio, target_speaker_embedding,
                        text=None):
        """
        将源音频的风格迁移到目标说话人
        
        参数:
            source_audio: 源音频(提供风格)
            target_speaker_embedding: 目标说话人嵌入(提供音色)
            text: 可选的文本(如果提供,使用文本控制内容)
        """
        # 提取源音频的韵律特征
        prosody = self._extract_prosody(source_audio)
        
        # 使用目标说话人的音色和源音频的韵律生成新语音
        output = self._synthesize_with_prosody(
            prosody, target_speaker_embedding, text
        )
        
        return output
    
    def _extract_prosody(self, audio):
        """提取韵律特征"""
        prosody = {
            "pitch": self.pitch_extractor.extract(audio),
            "energy": self._extract_energy(audio),
            "duration": self.duration_model.predict(audio)
        }
        return prosody
    
    def _extract_energy(self, audio):
        """提取能量包络"""
        import numpy as np
        frame_length = 512
        hop_length = 256
        
        frames = np.lib.stride_tricks.as_strided(
            audio,
            shape=(len(audio) // hop_length, frame_length),
            strides=(audio.strides[0] * hop_length, audio.strides[0])
        )
        
        energy = np.sqrt(np.mean(frames ** 2, axis=1))
        return energy
    
    def _synthesize_with_prosody(self, prosody, speaker_embedding, text):
        """使用指定韵律和音色合成语音"""
        # 实际实现需要条件TTS模型
        pass


class PitchExtractor:
    """音高提取器"""
    
    def extract(self, audio, sr=16000):
        """使用Parselmouth提取基频"""
        import parselmouth
        
        sound = parselmouth.Sound(audio, sr)
        pitch = sound.to_pitch()
        
        # 获取基频值
        pitch_values = pitch.selected_array['frequency']
        
        return pitch_values
    
    def modify_pitch(self, audio, pitch_factor, sr=16000):
        """
        修改音高
        
        参数:
            audio: 输入音频
            pitch_factor: 音高变化因子(>1升高,<1降低)
            sr: 采样率
        """
        import parselmouth
        
        sound = parselmouth.Sound(audio, sr)
        
        # 修改音高
        manipulation = parselmouth.praat.call(
            sound, "To Manipulation", 0.01, 75, 600
        )
        
        pitch_tier = manipulation.extract_pitch_tier()
        parselmouth.praat.call(
            pitch_tier, "Multiply frequencies",
            pitch_tier.xmin, pitch_tier.xmax, pitch_factor
        )
        
        manipulation.replace_pitch_tier(pitch_tier)
        
        # 重新合成
        output = parselmouth.praat.call(manipulation, "Get resynthesis (overlap-add)")
        
        return output.values.T.flatten()

七、音乐质量评估标准

7.1 客观评估指标

import numpy as np
import torch
from scipy import stats

class AudioQualityEvaluator:
    """音频质量评估器"""
    
    def __init__(self):
        self.metrics = {}
    
    def evaluate_all(self, generated_audio, reference_audio=None,
                     sr=16000):
        """执行全面质量评估"""
        results = {}
        
        # 1. 信号质量指标
        results["signal_quality"] = self.signal_quality_metrics(
            generated_audio, sr
        )
        
        # 2. 频谱质量
        results["spectral_quality"] = self.spectral_quality_metrics(
            generated_audio, sr
        )
        
        # 3. 如果有参考音频,计算相似度
        if reference_audio is not None:
            results["similarity"] = self.similarity_metrics(
                generated_audio, reference_audio, sr
            )
        
        # 4. 音乐特有指标
        results["music_quality"] = self.music_specific_metrics(
            generated_audio, sr
        )
        
        return results
    
    def signal_quality_metrics(self, audio, sr):
        """信号质量指标"""
        metrics = {}
        
        # 信噪比估计
        metrics["estimated_snr"] = self._estimate_snr(audio)
        
        # 动态范围
        metrics["dynamic_range"] = self._dynamic_range(audio)
        
        # 削波检测
        metrics["clipping_ratio"] = self._detect_clipping(audio)
        
        # RMS能量
        metrics["rms_energy"] = np.sqrt(np.mean(audio ** 2))
        
        return metrics
    
    def spectral_quality_metrics(self, audio, sr):
        """频谱质量指标"""
        import librosa
        
        metrics = {}
        
        # 频谱质心(音色亮度)
        spectral_centroid = librosa.feature.spectral_centroid(
            y=audio, sr=sr
        )
        metrics["spectral_centroid_mean"] = float(spectral_centroid.mean())
        
        # 频谱带宽
        spectral_bandwidth = librosa.feature.spectral_bandwidth(
            y=audio, sr=sr
        )
        metrics["spectral_bandwidth_mean"] = float(spectral_bandwidth.mean())
        
        # 频谱平坦度(噪声程度)
        spectral_flatness = librosa.feature.spectral_flatness(y=audio)
        metrics["spectral_flatness_mean"] = float(spectral_flatness.mean())
        
        # 零交叉率
        zcr = librosa.feature.zero_crossing_rate(audio)
        metrics["zcr_mean"] = float(zcr.mean())
        
        return metrics
    
    def similarity_metrics(self, generated, reference, sr):
        """与参考音频的相似度"""
        metrics = {}
        
        # 频谱距离
        metrics["spectral_distance"] = self._spectral_distance(
            generated, reference, sr
        )
        
        # MFCC相似度
        metrics["mfcc_similarity"] = self._mfcc_similarity(
            generated, reference, sr
        )
        
        # 时长差异
        duration_diff = abs(len(generated) - len(reference)) / sr
        metrics["duration_difference"] = duration_diff
        
        return metrics
    
    def music_specific_metrics(self, audio, sr):
        """音乐特有指标"""
        import librosa
        
        metrics = {}
        
        # 节奏估计
        tempo, _ = librosa.beat.beat_track(y=audio, sr=sr)
        metrics["estimated_tempo"] = float(tempo)
        
        # 调性估计
        chroma = librosa.feature.chroma_cqt(y=audio, sr=sr)
        key_profiles = self._get_key_profiles()
        
        best_key = None
        best_corr = -1
        
        for key_name, profile in key_profiles.items():
            chroma_mean = chroma.mean(axis=1)
            corr = np.corrcoef(chroma_mean, profile)[0, 1]
            if corr > best_corr:
                best_corr = corr
                best_key = key_name
        
        metrics["estimated_key"] = best_key
        metrics["key_confidence"] = float(best_corr)
        
        # 和声丰富度
        metrics["harmonic_complexity"] = float(chroma.std(axis=0).mean())
        
        return metrics
    
    def _estimate_snr(self, audio):
        """估计信噪比"""
        # 简化版:使用信号能量与噪声能量的比值
        signal_power = np.mean(audio ** 2)
        
        # 使用高通滤波估计噪声
        from scipy.signal import butter, filtfilt
        b, a = butter(4, 0.1, btype='high')
        noise = filtfilt(b, a, audio)
        noise_power = np.mean(noise ** 2)
        
        if noise_power > 0:
            snr = 10 * np.log10(signal_power / noise_power)
        else:
            snr = float('inf')
        
        return snr
    
    def _dynamic_range(self, audio):
        """计算动态范围"""
        max_val = np.max(np.abs(audio))
        min_val = np.min(np.abs(audio[audio != 0])) if np.any(audio != 0) else 1e-10
        
        return 20 * np.log10(max_val / min_val)
    
    def _detect_clipping(self, audio, threshold=0.99):
        """检测削波"""
        clipped_samples = np.sum(np.abs(audio) > threshold)
        total_samples = len(audio)
        
        return clipped_samples / total_samples
    
    def _spectral_distance(self, audio1, audio2, sr):
        """计算频谱距离"""
        import librosa
        
        spec1 = np.abs(librosa.stft(audio1))
        spec2 = np.abs(librosa.stft(audio2))
        
        # 调整长度
        min_len = min(spec1.shape[1], spec2.shape[1])
        spec1 = spec1[:, :min_len]
        spec2 = spec2[:, :min_len]
        
        # 对数频谱距离
        log_spec1 = np.log(spec1 + 1e-10)
        log_spec2 = np.log(spec2 + 1e-10)
        
        distance = np.sqrt(np.mean((log_spec1 - log_spec2) ** 2))
        
        return float(distance)
    
    def _mfcc_similarity(self, audio1, audio2, sr):
        """MFCC相似度"""
        import librosa
        
        mfcc1 = librosa.feature.mfcc(y=audio1, sr=sr, n_mfcc=13)
        mfcc2 = librosa.feature.mfcc(y=audio2, sr=sr, n_mfcc=13)
        
        # 计算均值的余弦相似度
        mean1 = mfcc1.mean(axis=1)
        mean2 = mfcc2.mean(axis=1)
        
        similarity = np.dot(mean1, mean2) / (
            np.linalg.norm(mean1) * np.linalg.norm(mean2) + 1e-10
        )
        
        return float(similarity)
    
    def _get_key_profiles(self):
        """获取大调和小调的调性轮廓"""
        # Krumhansl-Schmuckler调性轮廓
        major_profile = np.array([6.35, 2.23, 3.48, 2.33, 4.38, 4.09,
                                   2.52, 5.19, 2.39, 3.66, 2.29, 2.88])
        minor_profile = np.array([6.33, 2.68, 3.52, 5.38, 2.60, 3.53,
                                   2.54, 4.75, 3.98, 2.69, 3.34, 3.17])
        
        keys = {}
        note_names = ['C', 'C#', 'D', 'D#', 'E', 'F',
                      'F#', 'G', 'G#', 'A', 'A#', 'B']
        
        for i in range(12):
            keys[f"{note_names[i]} Major"] = np.roll(major_profile, i)
            keys[f"{note_names[i]} Minor"] = np.roll(minor_profile, i)
        
        return keys


class MusicGenerationEvaluator:
    """音乐生成质量评估"""
    
    # FAD (Frechet Audio Distance) 需要预训练的音频特征提取器
    # 这里提供简化版实现框架
    
    def fad_score(self, generated_features, reference_features):
        """
        Frechet Audio Distance
        
        参数:
            generated_features: 生成音频的特征 [N, D]
            reference_features: 参考音频的特征 [M, D]
        """
        # 计算均值和协方差
        mu_gen = np.mean(generated_features, axis=0)
        sigma_gen = np.cov(generated_features, rowvar=False)
        
        mu_ref = np.mean(reference_features, axis=0)
        sigma_ref = np.cov(reference_features, rowvar=False)
        
        # Frechet距离
        diff = mu_gen - mu_ref
        
        # 计算矩阵平方根
        covmean = self._matrix_sqrt(sigma_gen @ sigma_ref)
        
        fad = np.sum(diff ** 2) + np.trace(
            sigma_gen + sigma_ref - 2 * covmean
        )
        
        return float(fad)
    
    def _matrix_sqrt(self, matrix):
        """计算矩阵平方根"""
        eigenvalues, eigenvectors = np.linalg.eigh(matrix)
        sqrt_eigenvalues = np.sqrt(np.maximum(eigenvalues, 0))
        return eigenvectors @ np.diag(sqrt_eigenvalues) @ eigenvectors.T
    
    def kl_divergence_score(self, generated, reference):
        """
        KL散度评分
        比较生成音频和参考音频的频谱分布
        """
        from scipy.stats import entropy
        
        # 计算频谱
        spec_gen = np.abs(np.fft.fft(generated))
        spec_ref = np.abs(np.fft.fft(reference))
        
        # 归一化为概率分布
        spec_gen = spec_gen / (spec_gen.sum() + 1e-10)
        spec_ref = spec_ref / (spec_ref.sum() + 1e-10)
        
        # KL散度
        kl_div = entropy(spec_gen, spec_ref)
        
        return float(kl_div)


# 使用示例
evaluator = AudioQualityEvaluator()

# 生成音频评估
generated = np.random.randn(16000 * 5)  # 5秒音频
reference = np.random.randn(16000 * 5)

results = evaluator.evaluate_all(generated, reference, sr=16000)

print("音频质量评估结果:")
for category, metrics in results.items():
    print(f"\n{category}:")
    for metric, value in metrics.items():
        print(f"  {metric}: {value:.4f}")

八、版权与伦理问题

8.1 AI音乐的版权问题

class CopyrightChecker:
    """
    AI音乐版权检查器
    检查生成的音乐是否与已有作品相似
    """
    
    def __init__(self):
        self.reference_db = {}  # 参考音乐数据库
    
    def check_similarity(self, generated_audio, threshold=0.85):
        """
        检查生成音频与已有作品的相似度
        
        参数:
            generated_audio: 生成的音频
            threshold: 相似度阈值
        """
        import librosa
        
        # 提取特征
        features = self._extract_features(generated_audio)
        
        # 与数据库中的作品比较
        matches = []
        
        for ref_id, ref_features in self.reference_db.items():
            similarity = self._compute_similarity(features, ref_features)
            
            if similarity > threshold:
                matches.append({
                    "reference_id": ref_id,
                    "similarity": similarity,
                    "risk_level": "high" if similarity > 0.95 else "medium"
                })
        
        return {
            "is_original": len(matches) == 0,
            "matches": matches,
            "max_similarity": max([m["similarity"] for m in matches], default=0)
        }
    
    def _extract_features(self, audio, sr=16000):
        """提取音频特征用于比较"""
        import librosa
        
        # MFCC特征
        mfcc = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=13)
        
        # 色度特征(用于旋律比较)
        chroma = librosa.feature.chroma_cqt(y=audio, sr=sr)
        
        # 频谱特征
        spectral = librosa.feature.spectral_contrast(y=audio, sr=sr)
        
        return {
            "mfcc": mfcc.mean(axis=1),
            "chroma": chroma.mean(axis=1),
            "spectral": spectral.mean(axis=1)
        }
    
    def _compute_similarity(self, features1, features2):
        """计算两个特征向量的相似度"""
        # 组合特征
        vec1 = np.concatenate([
            features1["mfcc"],
            features1["chroma"],
            features1["spectral"]
        ])
        vec2 = np.concatenate([
            features2["mfcc"],
            features2["chroma"],
            features2["spectral"]
        ])
        
        # 余弦相似度
        similarity = np.dot(vec1, vec2) / (
            np.linalg.norm(vec1) * np.linalg.norm(vec2) + 1e-10
        )
        
        return float(similarity)
    
    def generate_copyright_report(self, audio_path, output_path):
        """生成版权检查报告"""
        import json
        
        # 加载音频
        audio, sr = librosa.load(audio_path, sr=16000)
        
        # 检查相似度
        results = self.check_similarity(audio)
        
        # 生成报告
        report = {
            "audio_file": audio_path,
            "check_date": str(np.datetime64('now')),
            "results": results,
            "recommendation": self._get_recommendation(results)
        }
        
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2, ensure_ascii=False)
        
        return report
    
    def _get_recommendation(self, results):
        """根据检查结果给出建议"""
        if results["is_original"]:
            return "版权风险低,建议保留生成记录作为证据。"
        elif results["max_similarity"] > 0.95:
            return "版权风险高!建议重新生成或修改。"
        else:
            return "存在一定相似性,建议人工审核。"


class EthicalGuidelines:
    """AI音频生成伦理指南"""
    
    GUIDELINES = {
        "数据使用": [
            "使用公开数据集或获得授权的数据训练模型",
            "尊重原始创作者的版权和署名权",
            "避免使用未经授权的个人声音数据",
        ],
        "内容生成": [
            "不生成冒充他人的语音",
            "不生成虚假或误导性音频内容",
            "标注AI生成的内容",
        ],
        "商业使用": [
            "了解各平台的使用条款",
            "在商业项目中使用时确保合规",
            "保留生成过程的记录",
        ],
        "隐私保护": [
            "不收集或存储用户的声音数据除非必要",
            "提供声音数据删除机制",
            "明确告知用户数据使用方式",
        ]
    }
    
    @classmethod
    def print_guidelines(cls):
        """打印伦理指南"""
        print("=" * 60)
        print("AI音频生成伦理指南")
        print("=" * 60)
        
        for category, items in cls.GUIDELINES.items():
            print(f"\n【{category}】")
            for i, item in enumerate(items, 1):
                print(f"  {i}. {item}")
    
    @classmethod
    def check_compliance(cls, usage_type, has_consent=False,
                          is_commercial=False):
        """
        检查使用是否合规
        
        参数:
            usage_type: 使用类型 (voice_clone/music_gen/tts)
            has_consent: 是否获得同意
            is_commercial: 是否商业使用
        """
        issues = []
        
        if usage_type == "voice_clone" and not has_consent:
            issues.append("语音克隆需要获得声音所有者的明确同意")
        
        if is_commercial and usage_type == "music_gen":
            issues.append("商业使用AI生成音乐需确认平台授权条款")
        
        if not issues:
            return {"compliant": True, "issues": []}
        
        return {"compliant": False, "issues": issues}

九、实战案例:构建完整的音频生成系统

9.1 综合音频生成Pipeline

"""
完整的AI音频生成系统
集成音乐生成、语音合成、语音克隆等功能
"""

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

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class AudioGenerationSystem:
    """AI音频生成系统"""
    
    def __init__(self, config=None):
        """
        初始化系统
        
        config: 配置字典
        """
        self.config = config or self._default_config()
        self._initialize_components()
    
    def _default_config(self):
        """默认配置"""
        return {
            "music": {
                "model": "musicgen-medium",
                "sample_rate": 32000,
                "max_duration": 30
            },
            "tts": {
                "model": "bark",
                "sample_rate": 24000,
                "default_language": "zh"
            },
            "voice_clone": {
                "model": "xtts_v2",
                "min_reference_duration": 6,
                "sample_rate": 24000
            },
            "output_dir": "outputs",
            "log_file": "generation_log.json"
        }
    
    def _initialize_components(self):
        """初始化各组件"""
        logger.info("初始化音频生成系统...")
        
        # 延迟加载(按需初始化)
        self._music_client = None
        self._tts_client = None
        self._clone_client = None
        
        # 创建输出目录
        os.makedirs(self.config["output_dir"], exist_ok=True)
        
        # 初始化日志
        self.generation_log = []
        
        logger.info("系统初始化完成")
    
    @property
    def music_client(self):
        """懒加载音乐生成客户端"""
        if self._music_client is None:
            logger.info("加载音乐生成模型...")
            # 这里可以加载MusicGen或其他模型
            # self._music_client = MusicGenClient(model_size="medium")
            self._music_client = "placeholder"  # 占位
        return self._music_client
    
    @property
    def tts_client(self):
        """懒加载TTS客户端"""
        if self._tts_client is None:
            logger.info("加载TTS模型...")
            # self._tts_client = BarkTTS()
            self._tts_client = "placeholder"
        return self._tts_client
    
    def generate_music(self, prompt, duration=15, style=None,
                        output_name=None):
        """
        生成音乐
        
        参数:
            prompt: 音乐描述
            duration: 时长(秒)
            style: 风格
            output_name: 输出文件名
        """
        logger.info(f"生成音乐: {prompt[:50]}...")
        
        # 构建完整提示词
        full_prompt = prompt
        if style:
            full_prompt += f", {style} style"
        
        # 生成文件名
        if output_name is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_name = f"music_{timestamp}.wav"
        
        output_path = os.path.join(self.config["output_dir"], output_name)
        
        # 实际生成(这里用占位代码)
        # music_client = self.music_client
        # audio = music_client.generate(full_prompt, duration=duration)
        # music_client.save_audio(audio, output_path)
        
        # 记录日志
        self._log_generation("music", {
            "prompt": full_prompt,
            "duration": duration,
            "output": output_path
        })
        
        logger.info(f"音乐生成完成: {output_path}")
        return output_path
    
    def generate_speech(self, text, speaker=None, language="zh",
                         output_name=None):
        """
        生成语音
        
        参数:
            text: 文本
            speaker: 说话人
            language: 语言
            output_name: 输出文件名
        """
        logger.info(f"生成语音: {text[:50]}...")
        
        if output_name is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_name = f"speech_{timestamp}.wav"
        
        output_path = os.path.join(self.config["output_dir"], output_name)
        
        # 实际生成
        # tts = self.tts_client
        # tts.generate(text, speaker=speaker, output_path=output_path)
        
        self._log_generation("speech", {
            "text": text,
            "speaker": speaker,
            "language": language,
            "output": output_path
        })
        
        logger.info(f"语音生成完成: {output_path}")
        return output_path
    
    def clone_voice(self, text, reference_audio, language="zh",
                     output_name=None):
        """
        语音克隆
        
        参数:
            text: 文本
            reference_audio: 参考音频路径
            language: 语言
        """
        logger.info(f"语音克隆: {text[:30]}...")
        
        # 检查参考音频
        if not os.path.exists(reference_audio):
            raise FileNotFoundError(f"参考音频不存在: {reference_audio}")
        
        if output_name is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_name = f"cloned_{timestamp}.wav"
        
        output_path = os.path.join(self.config["output_dir"], output_name)
        
        # 实际生成
        # clone_client = self.clone_client
        # clone_client.clone_and_speak(text, reference_audio, output_path)
        
        self._log_generation("voice_clone", {
            "text": text,
            "reference": reference_audio,
            "language": language,
            "output": output_path
        })
        
        logger.info(f"语音克隆完成: {output_path}")
        return output_path
    
    def generate_song(self, lyrics, style="流行", title=None):
        """
        生成完整歌曲
        
        参数:
            lyrics: 歌词
            style: 风格
            title: 标题
        """
        logger.info(f"生成歌曲: {title or '未命名'}")
        
        # 分析歌词结构
        structure = self._analyze_lyrics(lyrics)
        
        # 构建音乐描述
        music_desc = self._build_song_description(structure, style)
        
        # 生成歌曲
        output_path = self.generate_music(
            music_desc,
            duration=180,  # 3分钟
            style=style,
            output_name=f"song_{title or 'untitled'}.wav"
        )
        
        self._log_generation("song", {
            "lyrics": lyrics,
            "style": style,
            "title": title,
            "output": output_path
        })
        
        return output_path
    
    def _analyze_lyrics(self, lyrics):
        """分析歌词结构"""
        import re
        
        structure = {
            "sections": [],
            "total_lines": 0
        }
        
        for line in lyrics.split("\n"):
            line = line.strip()
            match = re.match(r'\[(.*?)\]', line)
            if match:
                structure["sections"].append({
                    "type": match.group(1),
                    "lines": []
                })
            elif line and structure["sections"]:
                structure["sections"][-1]["lines"].append(line)
                structure["total_lines"] += 1
        
        return structure
    
    def _build_song_description(self, structure, style):
        """构建歌曲描述"""
        sections = [s["type"] for s in structure["sections"]]
        return f"{style} song with {', '.join(sections)}"
    
    def batch_generate(self, tasks):
        """
        批量生成
        
        参数:
            tasks: 任务列表
        """
        results = []
        
        for i, task in enumerate(tasks):
            logger.info(f"处理任务 {i+1}/{len(tasks)}")
            
            task_type = task.get("type")
            
            try:
                if task_type == "music":
                    path = self.generate_music(**task["params"])
                elif task_type == "speech":
                    path = self.generate_speech(**task["params"])
                elif task_type == "clone":
                    path = self.clone_voice(**task["params"])
                elif task_type == "song":
                    path = self.generate_song(**task["params"])
                else:
                    raise ValueError(f"未知任务类型: {task_type}")
                
                results.append({
                    "task": task,
                    "status": "success",
                    "output": path
                })
            except Exception as e:
                logger.error(f"任务失败: {e}")
                results.append({
                    "task": task,
                    "status": "failed",
                    "error": str(e)
                })
        
        return results
    
    def _log_generation(self, gen_type, params):
        """记录生成日志"""
        entry = {
            "timestamp": datetime.now().isoformat(),
            "type": gen_type,
            "params": params
        }
        self.generation_log.append(entry)
        
        # 保存到文件
        log_path = os.path.join(
            self.config["output_dir"],
            self.config["log_file"]
        )
        with open(log_path, 'w', encoding='utf-8') as f:
            json.dump(self.generation_log, f, indent=2, ensure_ascii=False)
    
    def get_statistics(self):
        """获取生成统计"""
        stats = {
            "total_generations": len(self.generation_log),
            "by_type": {},
            "total_duration": 0
        }
        
        for entry in self.generation_log:
            gen_type = entry["type"]
            stats["by_type"][gen_type] = stats["by_type"].get(gen_type, 0) + 1
            
            if "duration" in entry["params"]:
                stats["total_duration"] += entry["params"]["duration"]
        
        return stats


# ==================== 使用示例 ====================

def main():
    """主函数"""
    
    # 初始化系统
    system = AudioGenerationSystem()
    
    # 示例1:生成背景音乐
    music_path = system.generate_music(
        "Peaceful piano melody with soft strings, "
        "perfect for meditation",
        duration=60,
        style="ambient"
    )
    
    # 示例2:生成语音
    speech_path = system.generate_speech(
        "欢迎来到AI音频生成的世界!今天我们将探索如何使用AI创作音乐和语音。",
        language="zh"
    )
    
    # 示例3:生成歌曲
    lyrics = """[Verse 1]
在数字的世界里
AI学会了歌唱
从0和1之间
诞生了美妙乐章

[Chorus]
这是科技的奇迹
这是梦想的力量
让音乐不再遥远
让创意自由飞翔"""
    
    song_path = system.generate_song(
        lyrics=lyrics,
        style="流行电子",
        title="数字之歌"
    )
    
    # 示例4:批量生成
    tasks = [
        {
            "type": "music",
            "params": {
                "prompt": "Upbeat electronic dance music",
                "duration": 30,
                "style": "EDM"
            }
        },
        {
            "type": "speech",
            "params": {
                "text": "第一段旁白内容",
                "language": "zh"
            }
        },
        {
            "type": "speech",
            "params": {
                "text": "第二段旁白内容",
                "language": "zh"
            }
        }
    ]
    
    results = system.batch_generate(tasks)
    
    # 打印统计
    stats = system.get_statistics()
    print(f"\n生成统计:")
    print(f"  总生成数: {stats['total_generations']}")
    print(f"  按类型: {stats['by_type']}")
    print(f"  总时长: {stats['total_duration']}秒")


if __name__ == "__main__":
    main()

9.2 Gradio Web界面

"""
AI音频生成系统Web界面
使用Gradio构建
"""

import gradio as gr
import numpy as np


def create_audio_demo(system):
    """创建音频生成系统界面"""
    
    def generate_music_ui(prompt, style, duration):
        """音乐生成UI回调"""
        try:
            output_path = system.generate_music(
                prompt=prompt,
                duration=int(duration),
                style=style
            )
            return output_path, "音乐生成成功!"
        except Exception as e:
            return None, f"错误: {str(e)}"
    
    def generate_speech_ui(text, language, speaker):
        """语音生成UI回调"""
        try:
            output_path = system.generate_speech(
                text=text,
                language=language,
                speaker=speaker if speaker else None
            )
            return output_path, "语音生成成功!"
        except Exception as e:
            return None, f"错误: {str(e)}"
    
    def generate_song_ui(lyrics, style, title):
        """歌曲生成UI回调"""
        try:
            output_path = system.generate_song(
                lyrics=lyrics,
                style=style,
                title=title if title else None
            )
            return output_path, "歌曲生成成功!"
        except Exception as e:
            return None, f"错误: {str(e)}"
    
    # 构建界面
    with gr.Blocks(title="AI音频生成系统", theme=gr.themes.Soft()) as demo:
        gr.Markdown("# 🎵 AI音频生成系统")
        gr.Markdown("一站式AI音乐、语音和歌曲生成平台")
        
        with gr.Tabs():
            # 音乐生成标签页
            with gr.TabItem("🎹 音乐生成"):
                with gr.Row():
                    with gr.Column():
                        music_prompt = gr.Textbox(
                            label="音乐描述",
                            placeholder="描述你想要的音乐风格和氛围...",
                            lines=3
                        )
                        music_style = gr.Dropdown(
                            label="预设风格",
                            choices=["流行", "摇滚", "电子", "古典", "爵士",
                                     "民谣", "R&B", "说唱", "氛围", "原声"],
                            value="流行"
                        )
                        music_duration = gr.Slider(
                            minimum=5, maximum=120, value=30,
                            label="时长(秒)"
                        )
                        music_btn = gr.Button("🎹 生成音乐", variant="primary")
                    
                    with gr.Column():
                        music_output = gr.Audio(label="生成结果")
                        music_status = gr.Textbox(label="状态", interactive=False)
            
            # 语音生成标签页
            with gr.TabItem("🗣️ 语音生成"):
                with gr.Row():
                    with gr.Column():
                        speech_text = gr.Textbox(
                            label="文本内容",
                            placeholder="输入要合成的文本...",
                            lines=5
                        )
                        speech_lang = gr.Dropdown(
                            label="语言",
                            choices=["zh", "en", "ja", "ko", "fr", "de"],
                            value="zh"
                        )
                        speech_speaker = gr.Dropdown(
                            label="说话人(可选)",
                            choices=["默认", "男声1", "女声1", "男声2", "女声2"],
                            value="默认"
                        )
                        speech_btn = gr.Button("🗣️ 生成语音", variant="primary")
                    
                    with gr.Column():
                        speech_output = gr.Audio(label="生成结果")
                        speech_status = gr.Textbox(label="状态", interactive=False)
            
            # 歌曲生成标签页
            with gr.TabItem("🎤 歌曲生成"):
                with gr.Row():
                    with gr.Column():
                        song_lyrics = gr.Textbox(
                            label="歌词",
                            placeholder="[Verse 1]\n第一段歌词...\n\n[Chorus]\n副歌...",
                            lines=10
                        )
                        song_style = gr.Dropdown(
                            label="风格",
                            choices=["流行", "摇滚", "电子", "说唱", "民谣", "R&B"],
                            value="流行"
                        )
                        song_title = gr.Textbox(
                            label="歌曲标题",
                            placeholder="输入歌曲标题"
                        )
                        song_btn = gr.Button("🎤 生成歌曲", variant="primary")
                    
                    with gr.Column():
                        song_output = gr.Audio(label="生成结果")
                        song_status = gr.Textbox(label="状态", interactive=False)
            
            # 语音克隆标签页
            with gr.TabItem("🎭 语音克隆"):
                with gr.Row():
                    with gr.Column():
                        clone_text = gr.Textbox(
                            label="要说的话",
                            placeholder="输入要用克隆声音说的文本...",
                            lines=3
                        )
                        clone_ref = gr.Audio(
                            label="参考音频(上传6秒以上的音频)",
                            type="filepath"
                        )
                        clone_lang = gr.Dropdown(
                            label="语言",
                            choices=["zh", "en", "ja"],
                            value="zh"
                        )
                        clone_btn = gr.Button("🎭 克隆并生成", variant="primary")
                    
                    with gr.Column():
                        clone_output = gr.Audio(label="生成结果")
                        clone_status = gr.Textbox(label="状态", interactive=False)
        
        # 绑定事件
        music_btn.click(
            fn=generate_music_ui,
            inputs=[music_prompt, music_style, music_duration],
            outputs=[music_output, music_status]
        )
        
        speech_btn.click(
            fn=generate_speech_ui,
            inputs=[speech_text, speech_lang, speech_speaker],
            outputs=[speech_output, speech_status]
        )
        
        song_btn.click(
            fn=generate_song_ui,
            inputs=[song_lyrics, song_style, song_title],
            outputs=[song_output, song_status]
        )
        
        # 示例
        gr.Markdown("## 💡 使用示例")
        gr.Examples(
            examples=[
                ["A cheerful acoustic guitar melody, morning sunshine feeling", "原声", 15],
                ["深沉的大提琴独奏,带有忧郁的情绪", "古典", 20],
                ["Energetic EDM with heavy bass drops", "电子", 30],
            ],
            inputs=[music_prompt, music_style, music_duration]
        )
    
    return demo


# 启动
if __name__ == "__main__":
    # system = AudioGenerationSystem()
    # demo = create_audio_demo(system)
    # demo.launch(server_name="0.0.0.0", server_port=7861)
    pass

十、最佳实践与技巧

10.1 音乐生成最佳实践

# 提示词编写技巧
MUSIC_PROMPT_TIPS = """
1. 描述风格和情绪:
   ✓ "Melancholic piano ballad with soft strings"
   ✗ "Sad music"

2. 指定乐器:
   ✓ "Acoustic guitar fingerpicking with light percussion"
   ✗ "Guitar music"

3. 描述节奏和速度:
   ✓ "Upbeat tempo at 120 BPM with driving rhythm"
   ✗ "Fast music"

4. 添加场景/氛围:
   ✓ "Perfect for a rainy evening, cozy and intimate"
   ✗ "Relaxing music"

5. 参考艺术家/风格:
   ✓ "In the style of lo-fi hip hop, chill beats to study to"
   ✗ "Hip hop"

6. 组合多个元素:
   ✓ "Jazz-influenced electronic music with saxophone samples, 
      moderate tempo, suitable for a cocktail bar atmosphere"
"""

# 常见问题解决
TROUBLESHOOTING = {
    "生成的音乐不够长": [
        "使用扩展功能(extend)追加更多段落",
        "在提示词中明确指定时长",
        "分段生成后拼接"
    ],
    "音质不佳": [
        "使用更大的模型(large > medium > small)",
        "调整guidance_scale参数",
        "增加生成后处理(EQ、压缩)"
    ],
    "风格不符": [
        "使用更详细的风格描述",
        "添加风格标签(style tags)",
        "提供参考音频作为条件"
    ],
    "人声质量差": [
        "指定vocal style(如'clear female vocal')",
        "使用专门的人声生成模型",
        "后期处理(去噪、EQ)"
    ]
}

10.2 语音合成最佳实践

# 语音合成质量优化
TTS_OPTIMIZATION = """
1. 文本预处理:
   - 处理数字和缩写("123" → "一百二十三")
   - 添加适当的标点符号控制停顿
   - 分句过长的文本

2. 说话人选择:
   - 匹配内容的情感基调
   - 考虑目标听众
   - 测试多个说话人

3. 参数调优:
   - temperature: 控制多样性(0.5-0.8较稳定)
   - speed: 调整语速(0.8-1.2)
   - pitch: 微调音高

4. 后处理:
   - 降噪
   - 音量归一化
   - 添加适当的混响(如果需要)
"""

# 文本预处理工具
class TextPreprocessor:
    """文本预处理器"""
    
    def __init__(self, language="zh"):
        self.language = language
    
    def preprocess(self, text):
        """预处理文本"""
        # 基础清理
        text = text.strip()
        
        # 数字转换
        text = self._convert_numbers(text)
        
        # 缩写展开
        text = self._expand_abbreviations(text)
        
        # 标点规范化
        text = self._normalize_punctuation(text)
        
        # 分句
        sentences = self._split_sentences(text)
        
        return sentences
    
    def _convert_numbers(self, text):
        """数字转文字"""
        import re
        
        def number_to_chinese(num_str):
            num = int(num_str)
            if num < 10:
                return "零一二三四五六七八九"[num]
            elif num < 100:
                return f"{'零一二三四五六七八九'[num // 10]}十{'零一二三四五六七八九'[num % 10] if num % 10 else ''}"
            else:
                return num_str  # 简化处理
        
        # 替换数字
        text = re.sub(r'\d+', lambda m: number_to_chinese(m.group()), text)
        
        return text
    
    def _expand_abbreviations(self, text):
        """展开缩写"""
        abbreviations = {
            "AI": "人工智能",
            "TTS": "语音合成",
            "NLP": "自然语言处理",
        }
        
        for abbr, full in abbreviations.items():
            text = text.replace(abbr, full)
        
        return text
    
    def _normalize_punctuation(self, text):
        """规范化标点符号"""
        # 确保句子结尾有标点
        if text and text[-1] not in "。!?,;":
            text += "。"
        
        return text
    
    def _split_sentences(self, text):
        """分句"""
        import re
        sentences = re.split(r'[。!?;\n]+', text)
        return [s.strip() for s in sentences if s.strip()]

10.3 性能优化建议

"""
性能优化建议
"""

OPTIMIZATION_GUIDE = {
    "GPU优化": [
        "使用半精度浮点数(FP16)减少显存占用",
        "启用模型CPU卸载处理大模型",
        "使用批量生成提高GPU利用率",
        "合理设置chunk_size平衡速度和质量"
    ],
    "内存优化": [
        "及时释放不需要的张量",
        "使用生成器而非列表处理大数据",
        "流式处理长音频"
    ],
    "速度优化": [
        "使用更快的采样器(DDIM > DDPM)",
        "减少推理步数(通常50步足够)",
        "使用模型蒸馏版本",
        "并行处理独立任务"
    ],
    "质量优化": [
        "使用更大的模型(如果资源允许)",
        "调整guidance_scale找到最佳平衡点",
        "多次生成选择最佳结果",
        "后处理提升音质"
    ]
}

十一、总结与展望

11.1 技术总结

AI音频生成在2024-2025年取得了令人瞩目的进展:

  1. 音乐生成:Suno和Udio等产品实现了从文本/歌词到完整歌曲的端到端生成
  2. 语音合成:Bark等模型支持多语言、多情感的自然语音生成
  3. 语音克隆:XTTS等模型实现了零样本语音克隆,只需几秒参考音频
  4. 开源生态:MusicGen、Bark等开源模型使得本地部署成为可能

11.2 未来趋势

  1. 更长音乐:从当前的几分钟扩展到完整专辑
  2. 实时交互:实时音乐生成和语音合成
  3. 多模态融合:结合视频、图像的多媒体内容生成
  4. 个性化定制:针对个人偏好的音乐和语音风格
  5. 专业工具集成:与DAW、视频编辑软件的深度集成
  6. 版权解决方案:区块链等技术用于版权追踪和授权

11.3 学习路径建议

初学者:
1. 使用Suno/Udio的Web界面体验音乐生成
2. 学习Bark/XTTS进行基础语音合成
3. 练习提示词工程

进阶者:
1. 使用MusicGen API进行音乐生成开发
2. 实现语音克隆功能
3. 构建完整的音频生成pipeline

专家级:
1. 训练自定义音乐/语音模型
2. 开发创新的音频应用
3. 研究音频生成的前沿技术

参考资源

  • 论文

    • "Simple and Controllable Music Generation" (MusicGen)
    • "Bark: A Transformer-based Text-to-Audio Model"
    • "XTTS: Cross-lingual Text-to-Speech"
    • "High Fidelity Neural Audio Compression" (EnCodec)
  • 开源项目

  • API服务

本教程涵盖了AI音乐与语音生成的核心技术和实践方法。随着技术的快速发展,建议持续关注最新的模型和工具更新,在实践中不断探索和创新。AI音频生成正在重新定义音乐创作和语音交互的边界,未来充满无限可能。

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录