吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1363|回复: 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] 纯文本查看 复制代码
1
2
# mac brew install tk-devel
$ brew install tk-devel


[Python] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#!/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] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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] 纯文本查看 复制代码
1
2
3
4
[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, 2025-1-22 21:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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