AI作曲DiffRhythm原理及本地部署

1.原理简介

最近AI在音乐生成方面的进展引起了极大的关注,但现有的方法面临着严重的限制。一些当前的生成模型只能合成人声或伴奏轨道。虽然一些模型可以生成组合的人声和伴奏,但它们通常依赖于精心设计的多阶段级联架构和复杂的数据管道,阻碍了可扩展性。此外,大多数系统仅限于生成短音乐片段而不是全长歌曲。此外,广泛使用的基于语言模型的方法受到推理速度慢的困扰。DiffRhythm是第一个基于潜在扩散模型的歌曲生成模型,能够在短短 10 秒内合成具有人声和伴奏的完整歌曲,持续时间长达 4m45s,保持高音乐性和可理解性。

尽管 DiffRhythm 具有卓越的功能,但它的设计简单而优雅:它无需复杂的数据准备,采用简单的模型结构,并且在推理过程中只需要歌词和样式提示。此外,其非自回归结构确保了快速的推理速度。这种简单性保证了 DiffRhythm 的可扩展性。

模型结构如下:

image-20250314161556143

有兴趣的同学可以阅读这篇论文:DiffRhythm: Blazingly Fast and Embarrassingly Simple
End-to-End Full-Length Song Generation with Latent Diffusion。

image-20250314161748615

简单总结一下原理:

DiffRhythm 是一种基于潜在扩散的端到端歌曲生成模型,它能够以 44.1 kHz 的采样率生成长达 4 分 45 秒的完整立体声音乐作品,并通过歌词和风格提示进行引导。该模型的主要特点如下:

  • 端到端生成: 与许多现有模型不同,DiffRhythm 能够同时生成人声和伴奏,无需复杂的级联架构或数据管道。
  • 简单高效: 模型结构简洁,数据预处理简单,推理速度快,使其易于扩展和应用。
  • 高质量输出: 即使生成完整的歌曲,DiffRhythm 也能保持高水平的音乐性和可理解性。

DiffRhythm 的关键组件

  • 变分自动编码器 (VAE): 用于学习音频的紧凑潜在表示,同时保留感知音频细节,从而降低扩散模型训练的计算需求。

  • 扩散 Transformer (DiT): 在 VAE 学习到的潜在空间中生成歌曲,通过迭代去噪的方式完成。具体过程如下:初始化: DiT 接收来自 VAE 编码器的潜在表示作为输入,并添加随机噪声。条件输入: DiT 的输入除了潜在表示外,还包括风格提示、时间步长和歌词信息。风格提示用于控制歌曲的风格,时间步长指示当前的去噪步骤,歌词用于控制人声内容。Transformer 解码器: DiT 由多个 LLaMA 解码器层堆叠而成,每个层包含注意力机制和前馈网络。去噪: 在每个时间步长,DiT 预测潜在表示中噪声的分布,并使用该分布更新潜在表示,逐步去除噪声。重复步骤: DiT 重复进行注意力机制和前馈网络计算,以及去噪步骤,直到潜在表示中的噪声被完全去除,最终生成完整的歌曲。

  • 歌词到潜在对齐机制: 用于解决全歌曲生成中的歌词和人声对齐问题,通过句级对齐方式实现高可理解性。具体步骤为:歌词预处理: 将歌词转换为音素序列,并与时间戳信息关联起来。音素到潜在对齐:初始化一个与潜在表示等长的潜在对齐序列,将其转换为音素序列。将音素序列覆盖到 对应的时间位置。

2.安装部署

安装需要的库:

sudo apt-get install espeak-ng

克隆项目:

git clone https://github.com/ASLP-lab/DiffRhythm.git
cd DiffRhythm

创建虚环境:

conda create -n diffrhythm python=3.10
conda activate diffrhythm

安装依赖:

pip install -r requirements.txt

简单运行源码示例:

bash scripts/infer_wav_ref.sh

或者输入提示进行音乐创作:

bash scripts/infer_prompt_ref.sh

用文字prompts示例:

azzy Nightclub Vibe, Pop Emotional Piano or Indie folk ballad, coming-of-age themes, acoustic guitar picking with harmonica interludes

无需音频参考!

生成纯音乐:

"Arctic research station, theremin auroras dancing with geomagnetic storms"  

3.编写图形界面

源码没有带着图形界面,使用不方便,这里我用gradio写了一个简单的界面:

import gradio as gr
import subprocess
import os
import shutil
from datetime import datetime
import sys 

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
# 配置参数
OUTPUT_DIR = "gradio_outputs"
os.makedirs(OUTPUT_DIR, exist_ok=True)

def generate_music(
    lrc_text,
    ref_audio,
    ref_prompt,
    use_chunked
):
    """核心音乐生成函数"""
    # ====== 输入验证 ======
    # 处理歌词输入
    if not lrc_text.strip():
        raise gr.Error("❗ 请输入歌词内容")
    lrc_content = lrc_text

    # 验证风格输入
    if not (ref_audio or ref_prompt):
        raise gr.Error("❗ 需要音频参考或文字描述")
    if ref_audio is not None and ref_prompt.strip() != "":
        raise gr.Error("❗ 请选择音频参考或文字描述中的一种方式")

    # ====== 创建临时目录 ======
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
    temp_dir = os.path.join(OUTPUT_DIR, timestamp)
    os.makedirs(temp_dir, exist_ok=True)

    try:
        # ====== 保存歌词文件 ======
        lrc_path = os.path.join(temp_dir, "input.lrc")
        with open(lrc_path, "w", encoding="utf-8") as f:
            f.write(lrc_content)

        # ====== 处理参考音频 ======
        ref_audio_path = None
        if ref_audio:
            if isinstance(ref_audio, dict):  # 适配新版Gradio
                ref_audio_path = ref_audio["name"]
            else:
                ref_audio_path = ref_audio
            ref_prompt = ""  # 强制清空文字描述
        
        # 验证逻辑
        if not ref_audio_path and not ref_prompt.strip():
            raise gr.Error("❗ 必须提供参考音频或风格描述!")
        if ref_audio_path and ref_prompt.strip():
            raise gr.Error("❗ 请选择一种风格参考方式!")

        # ====== 构建执行命令 ======
        cmd = [
            "python3", "infer/infer.py",
            "--lrc-path", lrc_path,
            "--audio-length", "95",
            "--repo_id", "ASLP-lab/DiffRhythm-base",
            "--output-dir", temp_dir
        ]
        if use_chunked:
            cmd.append("--chunked")
        if ref_audio_path:
            cmd += ["--ref-audio-path", ref_audio_path]
        else:
            cmd += ["--ref-prompt", ref_prompt]

        # ====== 执行生成命令 ======
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            check=True
        )

        # ====== 获取输出结果 ======
        output_path = os.path.join(temp_dir, "output.wav")
        if not os.path.exists(output_path):
            raise FileNotFoundError("生成结果文件不存在")

        # 返回两个相同的路径给音频组件和文件下载组件
        return output_path, output_path

    except subprocess.CalledProcessError as e:
        error_detail = f"""
        🚨 生成失败!
        命令: {' '.join(e.cmd)}
        错误信息: {e.stderr}
        """
        raise gr.Error(error_detail)
    except Exception as e:
        raise gr.Error(f"🚨 发生未知错误: {str(e)}")

# ====== 界面布局 ======
with gr.Blocks(title="AI音乐工作室", theme=gr.themes.Soft(), css=".dark {background: #f0f2f6}") as demo:
    gr.Markdown("""
    <div style="text-align: center; padding: 20px;">
        <h1 style="color: #2563eb;">🎵 AI音乐工作室</h1>
        <p style="color: #4b5563;">通过人工智能生成个性化音乐作品</p>
    </div>
    """)

    with gr.Row(variant="panel"):
        # 输入区
        with gr.Column(scale=1):
            # 歌词输入
            lrc_text = gr.Textbox(
                label="直接输入歌词(LRC格式)",
                placeholder="""示例格式:
[00:00.00] 开始你的音乐之旅
[00:05.00] 用代码创造旋律
[00:10.00] 人工智能生成音乐""",
                lines=8,
                max_lines=20,
                visible=True
            )

            # 风格控制区
            with gr.Tabs():
                with gr.Tab("🎧 音频参考"):
                    ref_audio = gr.Audio(
                        label="上传参考音频(支持WAV/MP3)",
                        type="filepath",
                        sources=["upload"],
                        waveform_options={"show_controls": True}
                    )
                with gr.Tab("📝 文字描述"):
                    ref_prompt = gr.Textbox(
                        label="音乐风格描述(英文)",
                        placeholder="例如:epic orchestral, dramatic strings, choir vocals",
                        lines=3
                    )

            # 高级选项
            with gr.Accordion("⚙️ 高级设置", open=False):
                use_chunked = gr.Checkbox(
                    label="启用分块解码(推荐生成长音频)",
                    value=True
                )

            generate_btn = gr.Button("🚀 开始生成", variant="primary")

        # 输出区
        with gr.Column(scale=1):
            gr.Markdown("### 🎧 生成结果")
            audio_output = gr.Audio(
                label="试听音频",
                interactive=False,
                waveform_options={"show_controls": True}
            )
            output_file = gr.File(
                label="下载文件",
                file_types=[".wav"],
                visible=True
            )

    # ====== 交互逻辑 ======
    generate_btn.click(
        fn=generate_music,
        inputs=[lrc_text, ref_audio, ref_prompt, use_chunked],
        outputs=[audio_output, output_file]
    )

# ====== 启动应用 ======
if __name__ == "__main__":
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        show_error=True,
        share=False
    )

image-20250314161231315

4.测试

用DeepSeek按照DiffRhythm要求的风格生成歌词,prompts:

请你帮我生成一首能用于作曲的歌词,表现出大学生积极向上,热爱学习,报效祖国的斗志。为了使生成的歌词能用于后面作曲,请你参考如下格式作词,格式参考如下:“00:16.53]我一大早 起不来
[00:19.82]我的眼睛 都没睁开
[00:22.69]蝴蝶 都飞起来
[00:26.61]畅快
[00:28.18]忽然听见
[00:29.20]好听的音乐
[00:32.35]就继续用力
[00:33.29]闭上 我的眼睛 baby
[00:39.37]这音乐 太动听 我怎么办
[00:43.05]我太开心 所有烦恼 被洗干净
[00:51.61]我一大早 就起来
[00:54.85]我的声音 都没放开
[00:57.79]歌声 却飞起来
[01:26.28]一道光 两支花 三个人 四分五裂
[01:29.58]六六大顺 七上八下”

生成的歌词:

# 青春的旋律
[00:00.00]清晨的阳光 洒在脸上
[00:03.25]唤醒沉睡的 梦想
[00:06.45]校园里回荡 知识的声浪
[00:09.70]我们心向远方
[00:12.90]教室里的灯光 闪烁希望
[00:16.15]书本散发墨香
[00:19.35]智慧的种子 在心中生长
[00:22.60]未来由我们开创
[00:25.80]实验室里 探索真相
[00:29.05]代码在指尖流淌
[00:32.25]创新的火花 点亮夜的迷茫
[00:35.50]真理等待丈量
[00:38.70]运动场 上挥洒汗水
[00:41.95]青春的力量无法掩盖
[00:45.15]团结拼搏 是我们的风采
[00:48.40]梦想展翅豪迈
[00:51.60]啊 祖国母亲 我们时刻准备着
[00:54.85]用知识为您添砖加瓦
[00:58.05]在时代的舞台上大放光芒
[01:01.30]让世界见证我们的力量

风格设置为:

Inspirational Rock

上传参考音频重新作曲,这里我用的是“那条街”做参考音频:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值