how to play wav file in python?

Question:

I tried pygame for playing wav file like this:

import pygame
pygame.init()

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

but It change the voice and I don’t know why!
I read this link solutions and can’t solve my problem with playing wave file!

for this solution I dont know what should I import?

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

and for this solution /dev/dsp dosen’t exist in new version of 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()

and when I tried pyglet It give me this error:

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
Asked By: nim4n

||

Answers:

You can use PyAudio. An example here on my Linux it works:

#!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()  
Answered By: zhangyangyu

The reason pygame changes your audio is mixer defaults to a 22k sample rate:

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

Your wav is probably 8k. So when pygame plays it, it plays roughly twice as fast. So specify your wav frequency in the init.

Pyglet has some problems correctly reading RIFF headers. If you have a very basic wav file (with exactly a 16 byte fmt block) with no other information in the fmt chunk (like ‘fact’ data), it works. But it makes no provision for additional data in the chunks, so it’s really not adhering to the RIFF interface specification.

Answered By: Gene

Works for me on Windows:
https://pypi.org/project/playsound/

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

NOTE: This has a bug in Windows where it doesn’t close the stream.
I’ve added a PR for a fix here:
https://github.com/TaylorSMarks/playsound/pull/53/commits/53240d970aef483b38fc6d364a0ae0ad6f8bf9a0

Answered By: driedler

PyGame has 2 different modules for playing sound and music, the pygame.mixer module and the pygame.mixer.music module. This module contains classes for loading Sound objects and controlling playback. The difference is explained in the documentation:

The difference between the music playback and regular Sound playback is that the music is streamed, and never actually loaded all at once. The mixer system only supports a single music stream at once.

If you want to play a single wav file, you have to initialize the module and create a pygame.mixer.Sound() object from the file. Invoke play() to start playing the file. Finally, you have to wait for the file to play.

Use get_length() to get the length of the sound in seconds and wait for the sound to finish:
(The argument to pygame.time.wait() is in milliseconds)

import pygame

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

Alternatively you can use pygame.mixer.get_busy to test if a sound is being mixed. Query the status of the mixer continuously in a loop:

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()
Answered By: Rabbid76

Windows

winsound

If you are a Windows user,the easiest way is to use winsound.You don’t even need to install it.

Not recommended, too few functions

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

If you are looking for more advanced functions, you can try mp3play.

Unluckily,mp3play is only available in Python2 and Windows.

If you want to use it on other platforms,use playsound despite its poor functions.If you want to use it in Python3,I will give you the modified version which is available on Python 3.(at the bottom of the answer)

Also,mp3play is really good at playing wave files, and it gives you more choices.

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

Cross-platform

playsound

Playsound is very easy to use,but it is not recommended because you can’t pause or get some infomation of the music, and errors often occurs.Unless other ways doesn’t work at all, you may try this.

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

pygame

I’m using this code and it works on Ubuntu 22.04 after my test.
If it doesn’t work on your machine, consider updating your pygame lib.

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

pyglet

This works on Windows but it doesn’t work on my Ubuntu, so I can do nothing.

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

Conclusion

It seems that you are using Linux,so playsound may be your choice.My code maybe cannot solve your problem by using pygame and pyglet,because I always use Windows.If none of the solutions work on your machine,I suggest you run the program on Windows…

To other users seeing my answer, I have done many tests among many libraries,so if you are using Windows,you may try mp3play which can play both mp3 and wave files, and mp3play is the most pythonic, easy, light-weight and functional library.

mp3play in Python3

just copy the code below and create a file named 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)
Answered By: yiheng
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.