在Python中播放WAV文件

oo7oh9g9  于 2023-06-20  发布在  Python
关注(0)|答案(5)|浏览(157)

我尝试PyGame来播放这样的WAV文件:

import pygame
pygame.init()

pygame.mixer.music.load("mysound.wav")
pygame.mixer.music.play()
pygame.event.wait()

但它改变了声音,我不知道为什么!我读了this link解决方案,但无法解决我播放wave文件的问题!
对于这个解决方案,我不知道我应该导入什么?

s = Sound() 
s.read('sound.wav') 
s.play()

对于这个解决方案,/dev/dsp在新版本的linux中不存在:

from wave import open as waveOpen
from ossaudiodev import open as ossOpen
s = waveOpen('tada.wav','rb')
(nc,sw,fr,nf,comptype, compname) = s.getparams( )
dsp = ossOpen('/dev/dsp','w')
try:
  from ossaudiodev import AFMT_S16_NE
except ImportError:
  if byteorder == "little":
    AFMT_S16_NE = ossaudiodev.AFMT_S16_LE
  else:
    AFMT_S16_NE = ossaudiodev.AFMT_S16_BE
dsp.setparameters(AFMT_S16_NE, nc, fr)
data = s.readframes(nf)
s.close()
dsp.write(data)
dsp.close()

当我尝试pyglet它给予我这个错误:

import pyglet

music = pyglet.resource.media('mysound.wav')
music.play()

pyglet.app.run()
--------------------------

nima@ca005 Desktop]$ python play.py
Traceback (most recent call last):
  File "play.py", line 4, in <module>
    music = pyglet.resource.media('mysound.wav')
  File "/usr/lib/python2.7/site-packages/pyglet/resource.py", line 587, in media
    return media.load(path, streaming=streaming)
  File "/usr/lib/python2.7/site-packages/pyglet/media/__init__.py", line 1386, in load
    source = _source_class(filename, file)
  File "/usr/lib/python2.7/site-packages/pyglet/media/riff.py", line 194, in __init__
    format = wave_form.get_format_chunk()
  File "/usr/lib/python2.7/site-packages/pyglet/media/riff.py", line 174, in get_format_chunk
    for chunk in self.get_chunks():
  File "/usr/lib/python2.7/site-packages/pyglet/media/riff.py", line 110, in get_chunks
    chunk = cls(self.file, name, length, offset)
  File "/usr/lib/python2.7/site-packages/pyglet/media/riff.py", line 155, in __init__
    raise RIFFFormatException('Size of format chunk is incorrect.')
pyglet.media.riff.RIFFFormatException: Size of format chunk is incorrect.
AL lib: ReleaseALC: 1 device not closed
lyr7nygr

lyr7nygr1#

你可以使用PyAudio。在我的Linux上的一个例子,它工作:

#!usr/bin/env python  
#coding=utf-8  

import pyaudio  
import wave  

#define stream chunk   
chunk = 1024  

#open a wav format music  
f = wave.open(r"/usr/share/sounds/alsa/Rear_Center.wav","rb")  
#instantiate PyAudio  
p = pyaudio.PyAudio()  
#open stream  
stream = p.open(format = p.get_format_from_width(f.getsampwidth()),  
                channels = f.getnchannels(),  
                rate = f.getframerate(),  
                output = True)  
#read data  
data = f.readframes(chunk)  

#play stream  
while data:  
    stream.write(data)  
    data = f.readframes(chunk)  

#stop stream  
stream.stop_stream()  
stream.close()  

#close PyAudio  
p.terminate()
ghhkc1vu

ghhkc1vu2#

在Windows上运行:https://pypi.org/project/playsound/

>>> from playsound import playsound
>>> playsound('/path/to/a/sound/file/you/want/to/play.wav')

注意:这在Windows中有一个错误,它不会关闭流。我在这里添加了一个修复的PR:https://github.com/TaylorSMarks/playsound/pull/53/commits/53240d970aef483b38fc6d364a0ae0ad6f8bf9a0

1wnzp6jl

1wnzp6jl3#

pygame更改音频的原因是mixer默认为22k采样率:

initialize the mixer module
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=4096): return None

你的wav可能是8k。所以当pygame玩它的时候,它的速度大约是它的两倍。所以在init中指定你的wav频率。
Pyglet在正确阅读RIFF头文件时有一些问题。如果你有一个非常基本的wav文件(只有一个16字节的fmt块),在fmt块中没有其他信息(比如“事实”数据),它就可以工作。但是它没有在块中提供额外的数据,所以它实际上没有遵守RIFF接口规范。

ljo96ir5

ljo96ir54#

PyGame有两个不同的模块用于播放声音和音乐,pygame.mixer模块和pygame.mixer.music模块。此模块包含用于加载Sound对象和控制播放的类。在文档中解释了差异:
音乐播放和常规声音播放之间的区别在于音乐是流式播放的,并且从未实际上一次全部加载。混音器系统一次只支持单个音乐流。
如果你想播放一个 wav 文件,你必须初始化模块并从文件中创建一个pygame.mixer.Sound()对象。调用play()开始播放文件。最后,您必须等待文件播放。
使用get_length()获取声音的长度(以秒为单位)并等待声音结束:(pygame.time.wait()的参数以毫秒为单位)

import pygame

pygame.mixer.init()
my_sound = pygame.mixer.Sound('mysound.wav')
my_sound.play()
pygame.time.wait(int(my_sound.get_length() * 1000))

或者,您可以使用pygame.mixer.get_busy来测试是否正在混合声音。循环连续查询搅拌机状态:

import pygame
pygame.init()

pygame.mixer.init()
my_sound = pygame.mixer.Sound('mysound.wav')
my_sound.play()

while pygame.mixer.get_busy():
    pygame.time.delay(10)
    pygame.event.poll()
km0tfn4u

km0tfn4u5#

Windows

winsound

如果你是Windows用户,最简单的方法就是使用winsound。你甚至不需要安装它。
不推荐,功能太少

import winsound
winsound.PlaySound("Wet Hands.wav", winsound.SND_FILENAME)  
# add winsound.SND_ASYNC flag if you want to wait for it. 
# like winsound.PlaySound("Wet Hands.wav", winsound.SND_FILENAME | winsound.SND_ASYNC)

mp3play

如果您正在寻找更高级的功能,您可以尝试mp3play。
不幸的是,mp3play仅在Python2和Windows中可用。
如果你想在其他平台上使用它,请使用playsound,尽管它的功能很差。如果你想在Python 3中使用它,我会给你修改后的版本,它可以在Python 3上使用。(在答案的底部)
还有,mp3play在播放wave文件方面真的很不错,它给了你更多的选择。

import time
import mp3play
music = mp3play.load("Wet Hands.wav")
music.play()
time.sleep(music.seconds())

跨平台

播放声音

Playsound非常容易使用,但不建议使用,因为您无法暂停或获取音乐的某些信息,并且经常出现错误。除非其他方法根本不起作用,否则您可以尝试使用此方法。

import playsound
playsound.playsound("Wet Hands.wav", block=True)

pygame

我正在使用这段代码,经过测试,它在Ubuntu 22.04上工作。如果它在你的机器上不起作用,考虑更新你的pygame库。

import pygame
pygame.mixer.init()
pygame.mixer.music.load("Wet Hands.wav")
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
    pass

pyglet

这在Windows上工作,但在我的Ubuntu上不工作,所以我什么也做不了。

import pyglet
import time
sound = pyglet.media.load("Wet Hands.wav", "Wet Hands.wav")
sound.play()
time.sleep(sound.duration)

总结

看起来你使用的是Linux,所以playsound可能是你的选择。我的代码可能无法通过使用pygamepyglet来解决你的问题,因为我总是使用Windows。如果没有解决方案在你的机器上工作,我建议你在Windows上运行程序。
对于看到我答案的其他用户,我在许多库中做了很多测试,所以如果你使用Windows,你可以尝试mp3play,它可以播放mp3和wave文件,mp3play是最pythonic,简单,轻量级和功能的库。

python3中的mp3play

只需复制下面的代码,并在工作目录中创建一个名为www.example.com的文件,然后粘贴内容。mp3play.py in your working directory and paste the content.

import random
from ctypes import windll, c_buffer

class _mci:
    def __init__(self):
        self.w32mci = windll.winmm.mciSendStringA
        self.w32mcierror = windll.winmm.mciGetErrorStringA

    def send(self, command):
        buffer = c_buffer(255)
        command = command.encode(encoding="utf-8")

        errorcode = self.w32mci(command, buffer, 254, 0)
        if errorcode:

            return errorcode, self.get_error(errorcode)
        else:
            return errorcode, buffer.value

    def get_error(self, error):
        error = int(error)
        buffer = c_buffer(255)
        self.w32mcierror(error, buffer, 254)
        return buffer.value

    def directsend(self, txt):
        (err, buf) = self.send(txt)
        # if err != 0:
        #     print('Error %s for "%s": %s' % (str(err), txt, buf))
        return err, buf

class _AudioClip(object):
    def __init__(self, filename):
        filename = filename.replace('/', '\\')
        self.filename = filename
        self._alias = 'mp3_%s' % str(random.random())

        self._mci = _mci()

        self._mci.directsend(r'open "%s" alias %s' % (filename, self._alias))
        self._mci.directsend('set %s time format milliseconds' % self._alias)

        err, buf = self._mci.directsend('status %s length' % self._alias)
        self._length_ms = int(buf)

    def volume(self, level):
        """Sets the volume between 0 and 100."""
        self._mci.directsend('setaudio %s volume to %d' %
                             (self._alias, level * 10))

    def play(self, start_ms=None, end_ms=None):
        start_ms = 0 if not start_ms else start_ms
        end_ms = self.milliseconds() if not end_ms else end_ms
        err, buf = self._mci.directsend('play %s from %d to %d'
                                        % (self._alias, start_ms, end_ms))

    def isplaying(self):
        return self._mode() == 'playing'

    def _mode(self):
        err, buf = self._mci.directsend('status %s mode' % self._alias)
        return buf

    def pause(self):
        self._mci.directsend('pause %s' % self._alias)

    def unpause(self):
        self._mci.directsend('resume %s' % self._alias)

    def ispaused(self):
        return self._mode() == 'paused'

    def stop(self):
        self._mci.directsend('stop %s' % self._alias)
        self._mci.directsend('seek %s to start' % self._alias)

    def milliseconds(self):
        return self._length_ms

    def __del__(self):
        self._mci.directsend('close %s' % self._alias)

_PlatformSpecificAudioClip = _AudioClip

class AudioClip(object):
    __slots__ = ['_clip']

    def __init__(self, filename):
        self._clip = _PlatformSpecificAudioClip(filename)

    def play(self, start_ms=None, end_ms=None):
        if end_ms is not None and end_ms < start_ms:
            return
        else:
            return self._clip.play(start_ms, end_ms)

    def volume(self, level):
        assert 0 <= level <= 100
        return self._clip.volume(level)

    def isplaying(self):
        return self._clip.isplaying()

    def pause(self):
        return self._clip.pause()

    def unpause(self):
        return self._clip.unpause()

    def ispaused(self):
        return self._clip.ispaused()

    def stop(self):
        return self._clip.stop()

    def seconds(self):
        return int(round(float(self.milliseconds()) / 1000))

    def milliseconds(self):
        return self._clip.milliseconds()

def load(filename):
    """Return an AudioClip for the given filename."""
    return AudioClip(filename)

相关问题