吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1157|回复: 13
收起左侧

[Python 原创] AI辅助编写的cue和wav音乐文件的歌曲分割提取

[复制链接]
testqkl 发表于 2024-9-5 18:19
本帖最后由 testqkl 于 2024-9-13 14:31 编辑

工具说明
cue和wav音乐文件的歌曲分割提取
ai辅助快速开发
通过百度的文小言辅助,自己修修改改的cue和wav音乐文件,需要先本地安装ffmpeg,如果不在path环境,可以自己写ffmpeg的全路径地址。

tikinter版本截图

image.png

Tkinter版本代码

[Python] 纯文本查看 复制代码
# mac brew install tk-devel
$ brew install tk-devel


[Python] 纯文本查看 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import (
    Tk,
    filedialog,
    messagebox,
    Label,
    Text,
    Button,
    END,
    Scrollbar,
    RIGHT,
    Y,
)
from functools import partial
import time
import os
import re
import subprocess
import json


class MUSIC_CUE_CUTE_GUI:
    def __init__(self, init_window_name):
        self.init_window_name = init_window_name
        self.cue_file_path = ""
        self.wav_file_path = ""
        self.wav_result_path = ""
        self.tracks = []
        self.ffmpeg = "ffmpeg"

    def reset_params(self):
        self.cue_file_path = ""
        self.wav_file_path = ""
        self.wav_result_path = ""
        self.tracks = []

    # 设置窗口
    def set_init_window(self):
        self.init_window_name.title("CUE音频提取器_v1.0.0")  # 窗口名
        # self.init_window_name.geometry('320x160+10+10')                         #290 160为窗口大小,+10 +10 定义窗口弹出时的默认展示位置
        self.init_window_name.geometry("681x681+10+10")
        # self.init_window_name["bg"] = "pink"                                    #窗口背景色,其他背景色见:blog.csdn.net/chl0000/article/details/7657887
        # self.init_window_name.attributes("-alpha",0.9)                          #虚化,值越小虚化程度越高
        # 标签

        # 按钮
        self.cue_file_button = Button(
            self.init_window_name,
            text="打开CUE文件",
            bg="lightblue",
            width=16,
            command=lambda: self.open_file("cue"),
        )  # 调用内部方法  加()为直接调用
        self.cue_file_button.grid(row=0, column=0)

        self.wav_file_button = Button(
            self.init_window_name,
            text="打开WAV文件",
            bg="lightblue",
            width=16,
            command=lambda: self.open_file("wav"),
        )  # 调用内部方法  加()为直接调用
        self.wav_file_button.grid(row=0, column=1)

        self.detect_ffmpeg_button = Button(
            self.init_window_name,
            text="检测ffmpeg环境",
            bg="lightblue",
            width=16,
            command=lambda: self.detect_ffmpeg(),
        )  # 调用内部方法  加()为直接调用
        self.detect_ffmpeg_button.grid(row=0, column=2)

        self.parse_cue_button = Button(
            self.init_window_name,
            text="读取cue歌曲信息",
            bg="lightblue",
            width=16,
            command=lambda: self.parse_cue(),
        )  # 调用内部方法  加()为直接调用
        self.parse_cue_button.grid(row=1, column=0)

        self.execute_button = Button(
            self.init_window_name,
            text=">> 提取音乐",
            bg="lightblue",
            width=16,
            command=lambda: self.split_audio(),
        )  # 调用内部方法  加()为直接调用
        self.execute_button.grid(row=1, column=1)

        self.result_data_label = Label(self.init_window_name, text="")
        self.result_data_label.grid(
            row=2, column=0, rowspan=1, columnspan=5, sticky="W"
        )

        self.result_data_Text = Text(self.init_window_name)  # 处理结果展示
        self.result_data_Text.grid(row=3, column=0, rowspan=5, columnspan=5)

        self.readme_text = Text(self.init_window_name, height=10)
        self.readme_text.grid(row=9, column=0, rowspan=2, columnspan=5, sticky="W")
        self.readme_text.insert(END, "使用说明:\n")
        self.readme_text.insert(END, "1. 请先检测ffmpeg环境\n")
        self.readme_text.insert(
            END, "2. 选择cue文件,如果wav和cue在同目录且同名,可不用操作wav文件\n"
        )
        self.readme_text.insert(END, "3. 尝试先读取cue文件的歌曲清单,做确认提取音乐\n")
        self.readme_text.insert(END, "4. 执行提取音乐\n")
        # self.readme_text.config(state="disable")

        # # 创建滚动条
        # scrollbar = Scrollbar(self.init_window_name)
        # scrollbar.pack(side=RIGHT, fill=Y)

        # # 将滚动条与Text组件关联
        # self.readme_text.config(yscrollcommand=scrollbar.set)
        # scrollbar.config(command=self.readme_text.yview)

    def detect_ffmpeg(self):
        try:
            subprocess.run([self.ffmpeg, "-version", ">", "/dev/null", "2>&1"])
            messagebox.showinfo("ffmpeg检测", "你已正确安装")
        except FileNotFoundError:
            messagebox.showwarning(
                "ffmpeg检测", "你未正确安装,请先安装ffmpeg到系统环境"
            )

    def open_file(self, t="cue"):
        filetypes = [("All files", "*.*")]
        if t == "cue":
            filetypes = [("Cue files", "*.cue")]
        elif t == "wav":
            filetypes = [("Wav files", "*.wav")]
        print(filetypes, t, self)
        file_path = filedialog.askopenfilename(
            title="选择" + t + "文件", filetypes=filetypes
        )
        if t == "cue":
            self.cue_file_path = file_path
            if self.wav_file_path == "":
                self.wav_file_path = self.replace_file_suffix(file_path, ".wav")
                self.wav_result_path = os.path.dirname(self.wav_file_path)
        elif t == "wav":
            if self.cue_file_path == "":
                self.cue_file_path = self.replace_file_suffix(file_path, ".cue")
            self.wav_file_path = file_path
            self.wav_result_path = os.path.dirname(self.wav_file_path)

        self.print_log("你选择了" + t + "文件: " + self.cue_file_path + "\n")
        self.print_log(
            "系统默认给你选择了"
            + ("wav" if t == "cue" else "cue")
            + "文件: "
            + self.wav_file_path
            + "\n",
        )

        print(self.cue_file_path, self.wav_file_path)

    def replace_file_suffix(self, file_path, new_suffix):
        # 使用os.path.splitext()分割文件路径为基本部分和扩展名
        base_name, ext = os.path.splitext(file_path)
        # 确保替换的是扩展名(去掉点号)
        if ext and ext[0] == ".":
            # 使用新的后缀替换旧的后缀,确保新后缀前有点号
            new_file_path = base_name + new_suffix
        else:
            # 如果没有找到扩展名,直接添加新后缀
            new_file_path = file_path + new_suffix
        return new_file_path

    def parse_cue(self):
        tracks = []
        current_track = None

        if self.cue_file_path == "":
            messagebox.showwarning("警告", "请先选择cue文件再来操作")

        with open(self.cue_file_path, "r", encoding="GB2312") as file:
            for line in file:
                line = line.strip()

                # 检查是否为新的音轨开始
                if line.startswith("TRACK"):
                    if current_track:
                        tracks.append(current_track)
                    match = re.match(r"TRACK (\d+) AUDIO", line)
                    if match:
                        track_number = int(match.group(1))
                        current_track = {
                            "number": track_number,
                            "title": "",
                            "start": None,
                        }

                # 提取歌曲名
                elif line.startswith("TITLE"):
                    title = line.split('TITLE "')[1].split('"')[0]
                    if current_track:
                        current_track["title"] = title

                # 提取起始时间
                elif line.startswith("INDEX 01"):
                    match = re.match(r"INDEX 01 ((\d+):(\d+):(\d+))", line)
                    if match:
                        # minutes, seconds, frames = map(int, match.groups())
                        # 将时间转换为毫秒
                        # total_seconds = minutes * 60 + seconds + frames / 75.0
                        # start_time = int(total_seconds * 1000)
                        start_time, minutes, seconds, ms = match.groups()
                        if current_track:
                            current_track["start"] = minutes + ":" + seconds + "." + ms

                # 注意:.cue文件通常不直接包含结束时间,但可以通过下一个音轨的起始时间推断

        # 添加最后一个音轨(如果有)
        if current_track:
            tracks.append(current_track)

        self.tracks = tracks

        self.print_log("提取到{}首音乐".format(len(self.tracks)))
        self.print_log(
            "\n".join(
                list(map(lambda x: json.dumps(x, ensure_ascii=False), self.tracks))
            )
        )

    def split_audio(self):
        if len(self.tracks) == 0:
            self.parse_cue()

        if len(self.tracks) == 0:
            self.print_log("cue音频提起失败")

        """根据 CUE 文件中的时间信息分割 WAV 文件"""
        for i, track in enumerate(self.tracks):
            # print(i, track)
            start_time = track["start"]

            # 使用 ffmpeg 剪切音频
            output_file = (
                self.wav_result_path + os.sep + track["title"].strip() + ".wav"
            )

            # 最后一个
            if len(self.tracks) == i + 1:
                # 如果是到文件末尾,则不需要指定结束时间
                cuteCmd = [
                    self.ffmpeg,
                    "-i",
                    self.wav_file_path,
                    "-y",
                    "-ss",
                    start_time,
                    "-vn",
                    "-acodec",
                    "copy",
                    output_file,
                ]
            else:
                # 如果是第一个音轨,没有前一个音轨的结束时间,所以从头开始
                next_track = self.tracks[i + 1]  # 获取前一个音轨的信息(注意索引)
                cuteCmd = [
                    self.ffmpeg,
                    "-i",
                    self.wav_file_path,
                    "-y",
                    "-ss",
                    start_time,
                    "-to",
                    next_track["start"],
                    "-vn",
                    "-acodec",
                    "copy",
                    output_file,
                ]

            self.print_log(i + 1 + ": " + " ".join(cuteCmd))
            subprocess.run(cuteCmd)

        self.print_log("cue音频提起完成")
        self.reset_params()

    # 获取当前时间
    def get_current_time(self):
        current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
        return current_time

    def print_log(self, text):
        self.result_data_Text.insert(END, self.get_current_time() + ": " + text + "\n")


def gui_start():
    init_window = Tk()  # 实例化出一个父窗口
    MUSIC_CUE_CUTE_GUI(init_window).set_init_window()
    init_window.mainloop()  # 父窗口进入事件循环,可以理解为保持窗口运行,否则界面不展示

if __name__ == "__main__":
    gui_start()




旧代码:
[Python] 纯文本查看 复制代码
import re
import subprocess
  
def parse_cue(cue_file_path):  
    tracks = []  
    current_track = None  
      
    with open(cue_file_path, 'r', encoding='GB2312') as file:  
        for line in file:  
            line = line.strip()  
              
            # 检查是否为新的音轨开始  
            if line.startswith('TRACK'):  
                if current_track:  
                    tracks.append(current_track)  
                match = re.match(r'TRACK (\d+) AUDIO', line)  
                if match:  
                    track_number = int(match.group(1))  
                    current_track = {'number': track_number, 'title': '', 'start': None, 'end': None}  
              
            # 提取歌曲名  
            elif line.startswith('TITLE'):  
                title = line.split('TITLE "')[1].split('"')[0]  
                if current_track:  
                    current_track['title'] = title  
              
            # 提取起始时间  
            elif line.startswith('INDEX 01'):  
                match = re.match(r'INDEX 01 ((\d+):(\d+):(\d+))', line)  
                if match:  
                    # minutes, seconds, frames = map(int, match.groups())  
                    # 将时间转换为毫秒  
                    # total_seconds = minutes * 60 + seconds + frames / 75.0  
                    # start_time = int(total_seconds * 1000)
                    start_time,minutes,seconds,ms = match.groups()
                    if current_track:  
                        current_track['start'] = minutes+":"+ seconds+"."+ms
              
            # 注意:.cue文件通常不直接包含结束时间,但可以通过下一个音轨的起始时间推断  
  
    # 添加最后一个音轨(如果有)  
    if current_track:  
        tracks.append(current_track)  
      
    return tracks

def split_audio(wav_file, tracks):  
    """ 根据 CUE 文件中的时间信息分割 WAV 文件 """  
    for i, track in enumerate(tracks):  
        print(i, track)
        start_time = track['start'] 
        if (len(tracks) ==  i+1):  
            # 如果是到文件末尾,则不需要指定结束时间  
            end_cmd = ''
            end_time = ''
        else:
            # 如果是第一个音轨,没有前一个音轨的结束时间,所以从头开始 
            next_track = tracks[i+1]  # 获取前一个音轨的信息(注意索引)
            end_cmd = '-to'
            end_time = next_track['start']
          
        # 使用 ffmpeg 剪切音频  
        output_file = track['title'].strip()+".wav"
        print(' '.join(['ffmpeg', '-ss', start_time, end_cmd, end_time, '-i', wav_file, '-y', '-vn', '-acodec', 'copy', output_file]))
        subprocess.run([  
            'ffmpeg', '-ss', start_time, end_cmd, end_time, '-i', wav_file, '-y', '-vn', '-acodec', 'copy', output_file  
        ])


# 使用示例  
cue_file_path = './1.cue'
wav_file = './1.wav'
tracks = parse_cue(cue_file_path)  
for track in tracks:  
    print(f"Track {track['number']}: {track['title']}, Start: {track['start']}")  
    # 注意:这里没有打印结束时间,因为它通常需要从下一个音轨的起始时间推断

confirm = input('确认分割音频:')
if confirm.lower() == 'y' or confirm.lower() == 'yes':
    split_audio(wav_file, tracks)
else:
    print("你已取消cue音频提取执行")

免费评分

参与人数 3吾爱币 +7 热心值 +3 收起 理由
苏紫方璇 + 5 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
勤快的懒虫 + 1 + 1 谢谢@Thanks!
helian147 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ot1686 发表于 2024-9-11 16:27
本帖最后由 ot1686 于 2024-9-11 16:30 编辑

大佬您的代码是不是有错误....因为假如是10首歌...但分割9首出来最后一首都有问题无法分割...小弟测试了几个cue.....都是少一首歌曲的....但....还是谢谢您..

都出现了以下问题

[Bash shell] 纯文本查看 复制代码
[AVFormatContext @ 0000015d2674e400] Unable to choose an output format for ''; use a standard extension for the filename or specify the format manually.
[out#0 @ 0000015d266bbac0] Error initializing the muxer for : Invalid argument
Error opening output file .
Error opening output files: Invalid argument
wkdxz 发表于 2024-9-6 09:29
本帖最后由 wkdxz 于 2024-9-6 09:30 编辑

我是这么做的:把CUE文件拖入foobar2000,即可自动提取出播放列表(包含歌曲名,演唱者等),然后选中歌曲右键转换为其他格式或者分割。

foobar2000 v2.2 preview 播放器下载
https://www.52pojie.cn/thread-1895502-1-1.html
(出处: 吾爱破解论坛)
无痕978 发表于 2024-9-5 21:43
miraak 发表于 2024-9-5 22:43
。。。。
这都要用ai?
amn2007 发表于 2024-9-6 05:09
参考一下
masteryun 发表于 2024-9-6 08:18
ffmpeg还能无损吗?
FishDreamer 发表于 2024-9-6 10:27
用foobar就可以
 楼主| testqkl 发表于 2024-9-6 11:46
wkdxz 发表于 2024-9-6 09:29
我是这么做的:把CUE文件拖入foobar2000,即可自动提取出播放列表(包含歌曲名,演唱者等),然后选中歌曲 ...

这个挺好,好像只有win,没有mac版本
 楼主| testqkl 发表于 2024-9-6 11:48
masteryun 发表于 2024-9-6 08:18
ffmpeg还能无损吗?

ffmpeg copy,应该是无损
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 14:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表