使用Python通过麦克风播放MP3文件

9
有没有一种使用Python(而不是任何外部软件)播放mp3文件的方法,就像麦克风输入一样?
例如,我有一个mp3文件,通过Python脚本播放它,其他人在语音房间里可以听到它。当然,这只是一个例子。
当然,我已经做了一些研究。我发现我可以使用软件创建虚拟设备并做一些事情来实现这个结果。但我的重点是,是否有可能用某种Python脚本而无需安装软件来实现呢?

不好意思,我不是指扬声器。我的问题与这个非常相似:http://stackoverflow.com/questions/28918079/how-to-play-mp3-files-into-the-microphone-input-jquery,唯一的区别是我想要一个Python的解决方案。 - Programer Beginner
然后,尝试以某种方式重新措辞问题或添加解释,因为它很令人困惑。你知道,麦克风和麦克风输入是两个不同的东西。我以为你希望使用麦克风而不是扬声器,这是可以做到的,但不可取,会损坏麦克风,我不明白为什么会有人这样做。我将在下面的编辑中回答你的问题。 - Dalen
你的意思是说,你想通过一个麦克风录制声音,并在多个扬声器中播放多人游戏吗?还是你想同时在不同的扬声器上播放预先录制的声音(mp3)?你使用“麦克风”这个词让我无法理解问题。 - Anil_M
我不认为立体声混音或虚拟电缆是正确的答案,尽管它们可能有效。我认为你试图找到的是一种手动控制麦克风输入的方法。我说得对吗? - BAKE ZQ
4个回答

16

在Python中,这是可能的,但需要安装其他软件。 (据我所知,这个具体的答案仅适用于Windows,但在Linux上应该类似,只需使用PulseAudio代替VB-Audio Cable即可,但我不是日常Linux用户,所以不确定。)

首先下载: https://www.vb-audio.com/Cable/,这将创建一个“虚拟音频线缆”,程序可以将音乐播放到输入设备(看起来像扬声器),然后将其传输到输出设备(看起来像麦克风)。

然后在cmd中运行此命令:pip install pygame==2.0.0.dev8(或者py -m pip install pygame==2.0.0.dev8,具体取决于您的Python安装方式)[同样之所以使用dev版本是因为它需要一些仅在sdl2中存在的函数,而主分支则使用sdl1]

然后:

>>> from pygame._sdl2 import get_num_audio_devices, get_audio_device_name #Get playback device names
>>> from pygame import mixer #Playing sound
>>> mixer.init() #Initialize the mixer, this will allow the next command to work
>>> [get_audio_device_name(x, 0).decode() for x in range(get_num_audio_devices(0))] #Returns playback devices
['Headphones (Oculus Virtual Audio Device)', 'MONITOR (2- NVIDIA High Definition Audio)', 'Speakers (High Definition Audio Device)', 'Speakers (NVIDIA RTX Voice)', 'CABLE Input (VB-Audio Virtual Cable)']
>>> mixer.quit() #Quit the mixer as it's initialized on your main playback device
>>> mixer.init(devicename='CABLE Input (VB-Audio Virtual Cable)') #Initialize it with the correct device
>>> mixer.music.load("Megalovania.mp3") #Load the mp3
>>> mixer.music.play() #Play it

停止音乐的方法是:mixer.music.stop()

此外,音乐不会通过扬声器播放,所以你需要另一个处理将其播放到扬声器的 Python 脚本或线程。(如果您想要在按按钮时播放它,我建议使用 Python 库 keyboard,GitHub 文档非常好,您应该能够自己解决它。)

附加说明:我花了一段时间才弄明白,不用谢。

另外,我仍在尝试找到一种方法来将您自己的麦克风输入导入其中,因为这种方法显然无法将您真正的麦克风输入导入,但是研究 Pygame 的源代码让我的头疼,因为它全部用 C 写成。


只需使用pyaudio代替pygame,您就可以在流和音频方面大显身手。如果虚拟设备不允许从同一进程打开多个流(一个用于从麦克风录制,另一个用于播放MP3),则缺点是必须手动混合音乐和录音。但这不应该如此。此外,MP3解码将需要单独执行,但如有需要,SDL和PortAudio可以很好地协同工作,尽管我认为整个pygame + pyaudio对此毫无意义。 - Dalen

4
如果你想知道如何使用Python播放MP3,那么这是一个广泛的问题。没有依赖关系,是可以实现的,但并不值得。播放未压缩音频可以,但对于MP3,我将在下面解释。要在Python中播放原始音频数据,而不安装pyaudio或pygame等类似软件包,请首先了解脚本运行的平台。然后实现一组不错的函数来选择音频设备、设置属性(如采样率、比特率、单声道/立体声等)、将流传输到音频卡并停止播放。这并不难,但要做到这一点, 您必须使用ctypes在Windows上,PyObjC在Mac上, Linux是一个特殊情况,因为它支持许多音频系统(可能使用套接字连接到PulseAudio或管道到一些进程,如aplay/paplay/mpeg123...或利用gstreamer)。但为什么要避免依赖而去做所有这些事情,当您有简单的库可用以访问和使用音频设备。PyAudio是个很棒的库。但是,纯Python实时从内部进行MP3播放,并没有外部库,这并不是完全不可能,但很难实现,据我所知,没有人尝试过这样做。有纯Python MP3解码器实现,但它的速度比实时音频播放所需的速度慢10倍。它可以优化到几乎全速,但没有人有兴趣这么做。它主要用于教育目的,并且在不需要实时速度的情况下使用。这是你应该做的:安装pygame并使用它直接播放MP3,或者安装PyAudio和一些解码Mp3的库,这些库在pypi.python.org上有很多,并使用它们将输出馈送到PyAudio。还有更多可能性,包括pymedia,但我认为这些是最简单的解决方案。好的,因为我们澄清了你真正需要的答案,我将保留第一个答案。现在,您想将音频播放到录音流中,以便录制音频输入的任何应用程序都记录您正在播放的内容。在Windows上,这称为立体声混音,并且可以在音量控制器下的音频输入中找到。选择立体声混音作为默认输入。现在,当您打开一个未选择其自己输入通道而使用所选通道(例如Skype)的录音应用程序时,它将记录从您的扬声器传出并进入麦克风/输入线的所有内容。我不确定此选项是否会出现在所有Windows上,还是您拥有的音频卡的功能。我肯定创新和瑞昱音频卡支持它。所以,请进行研究。
要从Python中选择该选项,您需使用ctypes连接到winmm.dll并调用相应的函数。我不知道是哪个函数以及需要什么参数。
如果音量控制中没有此选项,则只能安装虚拟音频卡来执行回路接口。
可能有这样的软件包作为库随机附带,以便您可以从Python或其他编程语言中使用它。
在Linux上,使用Pulseaudio应该很容易。我不知道如何做,但我知道您可以重定向流等操作,教程应该在某些地方。
然后,您可以从Python中调用该命令,设置为这个选项并恢复回去。
对于Mac系统,我真的不清楚,但应该是可能实现的。
如果您希望将MP3仅播放到录音流中,并且根本不在扬声器上播放,在Windows系统上,您将无法做到这一点,除非使用回路接口音频设备。
在Linux上,我确信您将能够实现它,在Mac系统上也应该是可能的,但具体如何操作还需要进一步了解。
目前我没有时间研究需要的代码,所以您必须自己完成。但是我希望我的指引能帮助您。

不,我不是指那样播放mp3文件。我的意思是通过麦克风播放mp3文件,这样在多人游戏中,我可以通过麦克风为其他人播放音乐。 - Programer Beginner

2

针对那些像我一样在实现这个过程中遇到困难的人,以下是关于@PyPylia回答的最新更新。

Current Package Version: pygame 2.1.2 (SDL 2.0.18, Python 3.10.6)

Tested Systems: Windows 10 (21H2 - 19044.1288), (Should be the same process on Mac but this is untested as of now...)

首先,您需要下载并安装适用于您平台的VB-Cable虚拟麦克风驱动程序。这将为我们提供一个虚拟麦克风,使我们能够在使用视频通话软件(如Google Meet、Microsoft Teams、Zoom)时将我们机器上播放的音频作为麦克风输入传递。之后,所有操作都通过pygame模块的音频包处理。
要获取音频设备列表:
from pygame import mixer, _sdl2 as devicer

mixer.init() # Initialize the mixer, this will allow the next command to work

# Returns playback devices, Boolean value determines whether they are Input or Output devices.
print("Inputs:", devicer.audio.get_audio_device_names(True))
print("Outputs:", devicer.audio.get_audio_device_names(False))

mixer.quit() # Quit the mixer as it's initialized on your main playback device

例如,我的设备返回:
Inputs: ['Microphone (High Definition Audio Device)', 'CABLE Output (VB-Audio Virtual Cable)']
Outputs: ['Speakers (High Definition Audio Device)', 'CABLE Input (VB-Audio Virtual Cable)']

然后,要播放音频:
import time
from pygame import mixer

mixer.init(devicename = 'CABLE Input (VB-Audio Virtual Cable)') # Initialize it with the correct device
mixer.music.load("Toby Fox - Megalovania.mp3") # Load the mp3
mixer.music.play() # Play it

while mixer.music.get_busy():  # wait for music to finish playing
    time.sleep(1)

如果您希望连续播放多个音轨,请在上述while循环中添加以下代码段:
...

else:
    mixer.music.unload() # Unload the mp3 to free up system resources

mixer.music.load("Sleeping at Last - Saturn.wav") # Load the wav

...

然后,在相关软件内的另一端,只需将麦克风输入从默认更改为 CABLE Output (VB-Audio Virtual Cable),即可让另一端听到源的音频。如果您使用的是较新版本的软件包,而某些列出的方法似乎因为 AttributeError: module 'pygame' has no attribute {method_name} 而无法使用,请使用 pyup 并搜索相关方法,以查看该方法的调用方式是否有任何更改。这是为什么 @PyPylia 的代码片段不再起作用,除非您使用旧版的 pygame

1
感谢您更新我的答案。我写这篇文章已经有一段时间了,现在的库已经更新了。此外,那时我的代码质量更差,所以感谢您以如此干净易读的方式编写它。 - PyPylia
我尝试按照这些步骤操作,但是我一直收到以下错误提示: pygame.error: 没有这样的设备。 编辑:我确定字符串是正确的,因为我从get_audio_device_names返回的列表中获取它。 - acornTime
@acornTime,你能私信给我你的代码片段和输出结果吗? - RavelRavencroft

0
如果您想播放本地目录中的音频文件,可以按照以下步骤进行操作。
#!/usr/bin/env python

import pyaudio
import socket
import sys
import os

CHUNK = 4096

output = os.path.join(BASE_DIR, "speech.wav") #WAV format Output file  name
wf = wave.open(output, 'rb')

p = pyaudio.PyAudio()

stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), 
    channels=wf.getnchannels(), 
    rate=wf.getframerate(), 
    output=True)

try:
    while True:  
        data = wf.readframes(CHUNK)
        stream.write(data)  
        
except KeyboardInterrupt:
    pass

print('Shutting down')
s.close()
stream.close()
audio.terminate()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接