首页 新闻 会员 周边

[python] pyinstaller生成可视化界面,ffmpeg转换音频运行subprocess.run()有命令窗口弹出

0
悬赏园豆:200 [已解决问题] 解决于 2024-08-05 09:48

python用tkinter写了一个文本转音频的可视化界面,音频转化用的是ffmpeg,具体代码如下

# 判断想要生成的音频类型 
        # 调用ffmpeg进行音频转换
        try:
            if audio_type == "mp3":
                command = create_mp3_command(file_path, save_path, sample_rate, bitrate, audio_channel)
               
                subprocess.run(command)
            else :
                command = create_wav_command(file_path, save_path, sample_rate, bitrate, sample_format, audio_channel)
                
                subprocess.run(command)

        
            # 裁剪生成的音频
            trim_silence(save_path, save_path, audio_type, sample_rate, bitrate)

            res = f"Audio saved in {save_path}"
            # print(res)
            return (True,res)

        except Exception as e:
            res = f"Exception occurred: {e}"
            # print(res)
            return (False, res)

在上面的代码中,我通过对想要生成的音频格式来生成不同的处理命令

  • 下面是mp3和wav的处理命令
# 返回ffmpeg的处理MP3的command
def create_mp3_command(file_path, save_path, sample_rate, bitrate, audio_channel):
    """
    构建 ffmpeg 命令的函数

    :param file_path: 输入文件路径 (str)
    :param save_path: 输出文件路径 (str)
    :param sample_rate: 采样率 (str)
    :param bitrate: 比特率 (str)
    :param sample_format: 采样格式 (str)
    :param audio_channel: 声道数 (str)
    :return: ffmpeg 命令列表 (list)
    """
    command = [
        "ffmpeg", 
        "-i", file_path, 
        "-loglevel", "quiet",       # 屏蔽ffmpeg log
        "-ar", sample_rate,         # 设置采样率为 16kHz
        "-b:a", bitrate,            # 设置比特率为 40kbps
        "-ac", audio_channel,       # 设置音频通道数为 2(立体声)
        "-c:a", "libmp3lame",       # 使用libmp3lame编码器
        "-q:a", "9",                # 设置质量参数为 9,确保使用恒定比特率
        "-filter:a", f"volume=1.5", # 调整音量
        "-y", save_path             # 覆盖输出文件
    ]
    return command


# 返回ffmpeg的处理wav的command
def create_wav_command(file_path, save_path, sample_rate, bitrate, sample_format, audio_channel):
    """
    构建 ffmpeg 命令的函数

    :param file_path: 输入文件路径 (str)
    :param save_path: 输出文件路径 (str)
    :param sample_rate: 采样率 (str)
    :param bitrate: 比特率 (str)
    :param sample_format: 采样格式 (str)
    :param audio_channel: 声道数 (str)
    :return: ffmpeg 命令列表 (list)
    """
    command = [
        "ffmpeg", 
        "-i", file_path, 
        "-loglevel", "quiet",           # 屏蔽 ffmpeg log
        "-ar", sample_rate,             # 采样率
        "-ac", audio_channel,           # 声道数
        "-b:a", bitrate,                # 比特率
        "-sample_fmt", data_list.BIT_DEPTH[sample_format],   # 采样格式
        "-filter:a", "volume=1.5",      # 调整音量
        "-y",                           # 覆盖输出文件
        save_path
    ]
    return command

根据MP3和wav生成不同的命令

在界面中,我有一个主界面,点击按钮button会开启子线程来运行该音频转换,并且弹出一个进度调的弹窗,代码如下:

        check_value = utils.check_value(speech_key, service_region, self.file_path, self.save_path)
        if check_value[0]:
            config.FILE_PATH = self.save_path
            # 初始化总进度数初始化为100
            progress_window = ProgressWindow(total_progress=100)
            threading.Thread(target=utils.start_transform, args=(progress_window, self.file_path, self.save_path, language, speed, audio_type, sample_rate, bitrate, sample_format, audio_channel)).start()
            progress_window.start_progress()
        else:
            messagebox.showerror("输入错误", check_value[1])


  • 然后问题来了,当我使用pyinstaller,将整个代码生成为exe程序后运行,最开始wav和mp3都会有命令窗口弹出,找了半天是subprocess.run的原因,于是我做了如下修改:
# 防止编译的exe出现命令窗口
subprocess.run(command, creationflags=subprocess.CREATE_NO_WINDOW)

在添加了creationflags=subprocess.CREATE_NO_WINDOW之后,我生成wav文件确实没有了命令弹窗,但是,当我切换到MP3,命令窗口还是没有消失

  • 于是我根据网上的搜索,又做了如下的操作,修改了mp3的subprocess.run代码
command = create_mp3_command(file_path, save_path, sample_rate, bitrate, audio_channel)

startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                
subprocess.run(command, startupinfo=startupinfo, creationflags=subprocess.CREATE_NO_WINDOW)

据说如此修改可以比上者更加有效的屏蔽命令窗口

但是以上方法都没有作用,mp3的subprocess还是会照样弹出命令窗口,有人遇到过此类问题吗?有什么解决方法吗?求帮助

qricis的主页 qricis | 菜鸟二级 | 园豆:224
提问于:2024-07-26 09:40
< >
分享
最佳答案
0

你这个wav和mp3命令没区别啊. wav用CREATE_NO_WINDOW可以,mp3应该也可以才对.是不是mp3忘了加CREATE_NO_WINDOW了

收获园豆:200
www378660084 | 小虾三级 |园豆:621 | 2024-07-26 11:40

加了的,运行出来wav没问题,mp3就是有弹窗,都快碎了,找不到原因

qricis | 园豆:224 (菜鸟二级) | 2024-07-26 11:53

@qricis: 我试了你的代码,没有你说的问题

import tkinter
import tkinter.messagebox
import subprocess

# 返回ffmpeg的处理MP3的command
def create_mp3_command(file_path, save_path, sample_rate, bitrate, audio_channel):
    """
    构建 ffmpeg 命令的函数

    :param file_path: 输入文件路径 (str)
    :param save_path: 输出文件路径 (str)
    :param sample_rate: 采样率 (str)
    :param bitrate: 比特率 (str)
    :param sample_format: 采样格式 (str)
    :param audio_channel: 声道数 (str)
    :return: ffmpeg 命令列表 (list)
    """
    command = [
        "ffmpeg",
        "-i", file_path,
        "-loglevel", "quiet",       # 屏蔽ffmpeg log
        "-ar", sample_rate,         # 设置采样率为 16kHz
        "-b:a", bitrate,            # 设置比特率为 40kbps
        "-ac", audio_channel,       # 设置音频通道数为 2(立体声)
        "-c:a", "libmp3lame",       # 使用libmp3lame编码器
        "-q:a", "9",                # 设置质量参数为 9,确保使用恒定比特率
        "-filter:a", f"volume=1.5", # 调整音量
        "-y", save_path             # 覆盖输出文件
    ]
    return command


# 返回ffmpeg的处理wav的command
def create_wav_command(file_path, save_path, sample_rate, bitrate, sample_format, audio_channel):
    """
    构建 ffmpeg 命令的函数

    :param file_path: 输入文件路径 (str)
    :param save_path: 输出文件路径 (str)
    :param sample_rate: 采样率 (str)
    :param bitrate: 比特率 (str)
    :param sample_format: 采样格式 (str)
    :param audio_channel: 声道数 (str)
    :return: ffmpeg 命令列表 (list)
    """
    command = [
        "ffmpeg",
        "-i", file_path,
        "-loglevel", "quiet",           # 屏蔽 ffmpeg log
        "-ar", sample_rate,             # 采样率
        "-ac", audio_channel,           # 声道数
        "-b:a", bitrate,                # 比特率
        "-sample_fmt", sample_format,   # 采样格式
        "-filter:a", "volume=1.5",      # 调整音量
        "-y",                           # 覆盖输出文件
        save_path
    ]
    return command


audio_type = "wav"
file_path = "dtmf.wav"
save_path = "dtmf_out.wav"
sample_rate = "8000"
bitrate = "40000"
audio_channel = "1"
sample_format = "s16"

root = tkinter.Tk()
def mp3():
    command = create_mp3_command(file_path, save_path, sample_rate, bitrate, audio_channel)
    subprocess.run(command, creationflags=subprocess.CREATE_NO_WINDOW)
btnMp3 = tkinter.Button(root, text='mp3', command=mp3)
btnMp3.place(x=30, y=70, width=50, height=20)
def wav():
    command = create_wav_command(file_path, save_path, sample_rate, bitrate, sample_format, audio_channel)
    subprocess.run(command, creationflags=subprocess.CREATE_NO_WINDOW)
btnWav = tkinter.Button(root, text='wav', command=wav)
btnWav.place(x=90, y=70, width=50, height=20)
root.mainloop()
www378660084 | 园豆:621 (小虾三级) | 2024-07-26 16:41

@www378660084: 我试了你的没有问题,但是我的是会弹出的,是因为试运行在的子线程

![](https://img2024.cnblogs.com/blog/3491085/202407/3491085-20240726172900952-994337261.png)

这一点前提忘记说了!!

qricis | 园豆:224 (菜鸟二级) | 2024-07-26 17:29

@qricis: 我觉得跟子线程也没关系.可能跟你进度条的实现有点关系,你可以在这个基础上试试,看看添加什么代码会弹出窗口

import tkinter
import tkinter.messagebox
import subprocess
import threading

# 返回ffmpeg的处理MP3的command
def create_mp3_command(file_path, save_path, sample_rate, bitrate, audio_channel):
    """
    构建 ffmpeg 命令的函数

    :param file_path: 输入文件路径 (str)
    :param save_path: 输出文件路径 (str)
    :param sample_rate: 采样率 (str)
    :param bitrate: 比特率 (str)
    :param sample_format: 采样格式 (str)
    :param audio_channel: 声道数 (str)
    :return: ffmpeg 命令列表 (list)
    """
    command = [
        "ffmpeg",
        "-i", file_path,
        "-loglevel", "quiet",       # 屏蔽ffmpeg log
        "-ar", sample_rate,         # 设置采样率为 16kHz
        "-b:a", bitrate,            # 设置比特率为 40kbps
        "-ac", audio_channel,       # 设置音频通道数为 2(立体声)
        "-c:a", "libmp3lame",       # 使用libmp3lame编码器
        "-q:a", "9",                # 设置质量参数为 9,确保使用恒定比特率
        "-filter:a", f"volume=1.5", # 调整音量
        "-y", save_path             # 覆盖输出文件
    ]
    return command


# 返回ffmpeg的处理wav的command
def create_wav_command(file_path, save_path, sample_rate, bitrate, sample_format, audio_channel):
    """
    构建 ffmpeg 命令的函数

    :param file_path: 输入文件路径 (str)
    :param save_path: 输出文件路径 (str)
    :param sample_rate: 采样率 (str)
    :param bitrate: 比特率 (str)
    :param sample_format: 采样格式 (str)
    :param audio_channel: 声道数 (str)
    :return: ffmpeg 命令列表 (list)
    """
    command = [
        "ffmpeg",
        "-i", file_path,
        "-loglevel", "quiet",           # 屏蔽 ffmpeg log
        "-ar", sample_rate,             # 采样率
        "-ac", audio_channel,           # 声道数
        "-b:a", bitrate,                # 比特率
        "-sample_fmt", sample_format,   # 采样格式
        "-filter:a", "volume=1.5",      # 调整音量
        "-y",                           # 覆盖输出文件
        save_path
    ]
    return command


audio_type = "wav"
file_path = "dtmf.wav"
save_path = "dtmf_out.wav"
sample_rate = "8000"
bitrate = "40000"
audio_channel = "1"
sample_format = "s16"

root = tkinter.Tk()
def mp3():
    def go():
        command = create_mp3_command(file_path, save_path, sample_rate, bitrate, audio_channel)
        subprocess.run(command, creationflags=subprocess.CREATE_NO_WINDOW)
    threading.Thread(target=go).start()
btnMp3 = tkinter.Button(root, text='mp3', command=mp3)
btnMp3.place(x=30, y=70, width=50, height=20)
def wav():
    def go():
        command = create_wav_command(file_path, save_path, sample_rate, bitrate, sample_format, audio_channel)
        subprocess.run(command, creationflags=subprocess.CREATE_NO_WINDOW)
    threading.Thread(target=go).start()
btnWav = tkinter.Button(root, text='wav', command=wav)
btnWav.place(x=90, y=70, width=50, height=20)
root.mainloop()
www378660084 | 园豆:621 (小虾三级) | 2024-07-26 19:22

@www378660084: 我刚尝试把所有与progress_window的代码都屏蔽了,程序还是会弹出命令窗口o(╥﹏╥)o,所以跟进度条还是没关系,会跟代码分几个py文件写有关吗?

qricis | 园豆:224 (菜鸟二级) | 2024-08-01 17:53

@www378660084: 在调用ffmpeg,是在循环当中的,以下是循环的代码

# 当生成的音频文件数量小于获取到的表格中的行数时,循环
    while(total_progress):
        # 获取两者中的非交集
        files_list = list(processed_set-set(files))
        # 将非交集的数据生成音频
        for file in files_list:
            # 文本转音频
            result = azure_utils.synthesize_speech_with_ssml(file, language, speed, audio_type)
            # 使用ffmpeg对音频进行处理
            ffpmeg_result = ffmpeg_utils.get_ffmpeg_para(result, file, audio_type, sample_rate, bitrate, sample_format, audio_channel)
        
            if ffpmeg_result[0]:
                pg_window.update_progress(ffpmeg_result[1])
                pg_window.increment_progress()
            else:
                pg_window.update_progress(ffpmeg_result[1])
                pg_window.finish_button.config(state=tk.NORMAL)
                delete_dir(config.TEMP)
                delete_dir(config.RELEASE)
                return

        # 重新计算生成的音频文件数量
        files = selectFile(save_path)
        # 初始化进度条的长度
        total_progress = len(processed_set - set(files))
        if (total_progress > 0) :
            pg_window.reset_progress()
            pg_window.set_progress(total_progress)
        else :
            pg_window.update_progress("Finish")
            delete_dir(config.TEMP)
            break

    pg_window.finish_button.config(state=tk.NORMAL)
qricis | 园豆:224 (菜鸟二级) | 2024-08-01 17:59

几天把代码拆解了,终于找到了命令窗口弹出的原因,不在之前的界面,在运行之后的音频裁剪部分:

# 裁剪音频,将整段音频的前后空白处裁剪到20ms左右
def trim_silence(audio_path, output_path, audio_type, sample_rate, bitrate, silence_duration=100, trim_buffer=20, silence_thresh=-30, chunk_size=10):
    # 加载音频文件
    audio = AudioSegment.from_file(audio_path)
    
    # 检测非静音部分
    nonsilent_ranges = detect_nonsilent(audio, min_silence_len=chunk_size, silence_thresh=silence_thresh)
    
    # 如果找到非静音部分
    if nonsilent_ranges:
        start_trim = max(0, nonsilent_ranges[0][0] - trim_buffer)
        end_trim = min(len(audio), nonsilent_ranges[-1][1] + trim_buffer)
        
        # 修剪音频
        trimmed_audio = audio[start_trim:end_trim]
    else:
        # 如果整段音频都是静音,返回原音频
        trimmed_audio = audio
    
    # 创建指定时长的静音
    silence = AudioSegment.silent(duration=int(silence_duration), frame_rate=int(sample_rate))
    
    # 将静音添加到音频文件的前后
    padded_audio = silence + trimmed_audio + silence
    
    # 删除旧文件(如果存在)
    if os.path.exists(output_path):
        os.remove(output_path)
    
    # 保存添加静音后的音频文件
    padded_audio.export(output_path, format=audio_type, bitrate=bitrate)

以上代码造成了mp3文件输出反复出现弹窗,尝试查找原因

qricis | 园豆:224 (菜鸟二级) | 2024-08-02 14:31

原因已找到,在上述代码中,有两行代码可能内部调用了ffmpeg的命令,因此造成了命令窗口的弹出:

# 裁剪音频,将整段音频的前后空白处裁剪到20ms左右
def trim_silence(audio_path, output_path, audio_type, sample_rate, bitrate, silence_duration=100, trim_buffer=20, silence_thresh=-30, chunk_size=10):
    # 加载音频文件 内部调用了ffmpeg
    audio = AudioSegment.from_file(audio_path)
# 保存添加静音后的音频文件 内部同样调用了ffmpeg
    padded_audio.export(output_path, format=audio_type, bitrate=bitrate)

将上述代码修改为直接使用ffmpeg即可:

# 裁剪音频前后端的静音部分
def trim_silence(audio_path, output_path, audio_type, sample_rate, bitrate, silence_duration=100, silence_thresh=-60, chunk_size=10):
    # 临时文件
    temp_output_start_trim = "temp_output_start_trim." + audio_type
    temp_output_end_trim = "temp_output_end_trim." + audio_type

    # 去除开头静音
    input_audio = ffmpeg.input(audio_path)
    filtered_audio_start_trim = (
        input_audio
        .filter('silenceremove', start_periods=1, start_duration=chunk_size / 1000, start_threshold=f'{silence_thresh}dB', 
                stop_periods=0, stop_duration=0, stop_threshold=f'{silence_thresh}dB')
        .output(temp_output_start_trim, ar=sample_rate, ac=1, b=bitrate, format=audio_type)
        .overwrite_output()
    )
    command = ffmpeg.compile(filtered_audio_start_trim)
    subprocess.run(command, creationflags=subprocess.CREATE_NO_WINDOW)

    # 去除结尾静音
    input_audio_trimmed = ffmpeg.input(temp_output_start_trim)
    filtered_audio_end_trim = (
        input_audio_trimmed
        .filter('silenceremove', start_periods=0, start_duration=0, start_threshold=f'{silence_thresh}dB', 
                stop_periods=1, stop_duration=chunk_size / 1000, stop_threshold=f'{silence_thresh}dB')
        .output(temp_output_end_trim, ar=sample_rate, ac=1, b=bitrate, format=audio_type)
        .overwrite_output()
    )
    command = ffmpeg.compile(filtered_audio_end_trim)
    subprocess.run(command, creationflags=subprocess.CREATE_NO_WINDOW)


    # 加载处理后的音频并添加静音部分
    trimmed_audio = ffmpeg.input(temp_output_end_trim)
    silence = ffmpeg.input('anullsrc=r=' + sample_rate, f='lavfi', t=silence_duration / 1000)
    
    # 创建输出音频,将静音部分添加到前后
    padded_audio = ffmpeg.concat(silence, trimmed_audio, silence, v=0, a=1)
    output_audio = padded_audio.output(output_path, ar=sample_rate, ac=1, b=bitrate, format=audio_type).overwrite_output()

    # 运行FFmpeg命令并捕获错误
    # run_ffmpeg_command(output_audio)
    command = ffmpeg.compile(output_audio)
    subprocess.run(command, creationflags=subprocess.CREATE_NO_WINDOW)

    # 删除临时文件
    if os.path.exists(temp_output_start_trim):
        os.remove(temp_output_start_trim)
    if os.path.exists(temp_output_end_trim):
        os.remove(temp_output_end_trim)

特此记录

qricis | 园豆:224 (菜鸟二级) | 2024-08-05 09:45
其他回答(1)
0

已找到问题所在地,详情请见内部评论

qricis | 园豆:224 (菜鸟二级) | 2024-08-05 09:45

应该是你用的这个库在加载mp3信息时候调用了cmd,这个库有人提过这个问题 https://github.com/jiaaro/pydub/issues/698

支持(0) 反对(0) www378660084 | 园豆:621 (小虾三级) | 2024-08-22 18:58

@www378660084: 谢谢!我去看看

支持(0) 反对(0) qricis | 园豆:224 (菜鸟二级) | 2024-08-26 16:37
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册