Search code examples
pythonwindowsctypesplaysound

No sound is generated when using PlaySound()


Basically I want to use the PlaySound() function in ctypes.

Yes I know winsound module is built on it and I could use that, but I have a reason to not do so :)


In C I would call the function like this:

PlaySound("sound.wav", NULL, SND_FILENAME);

I have my Python script equivalent:

import ctypes

winmm = ctypes.WinDLL('winmm.dll')

winmm.PlaySound("sound.wav", None, 0x20000)

I run it, no error is returned but the sound does not play either.


I suspect that the problem lies with the hex value (0x20000) since everything else seems fine. I got this value like this:

import winsound
print(hex(winsound.SND_FILENAME))

Or in a different way:

import ctypes, winsound

winmm = ctypes.WinDLL('winmm.dll')

winmm.PlaySound("sound.wav", None, winsound.SND_FILENAME)

So how can I get this working so that my file plays?


Solution

  • In Windows, there are Unicode and ANSI versions of functions. The documentation indicates the filename is an LPCTSTR. For the ANSI version that is defined as LPCSTR and for Unicode it is LPCWSTR.

    Here's the proper way to call a Windows function. Generally you want the W version of the function. Defining .argtypes and .restype will also help with error checking. As you found, you can pass the wrong type and it will not work. With .argtypes defined, incompatible types will be caught.

    from ctypes import *
    from ctypes import wintypes as w
    
    dll = WinDLL('winmm')
    
    dll.PlaySoundW.argtypes = w.LPCWSTR,w.HMODULE,w.DWORD
    dll.PlaySoundW.restype = w.BOOL
    
    SND_FILENAME = 0x20000
    
    # Call it with a Unicode string and it works.
    dll.PlaySoundW('sound.wav',None,SND_FILENAME)
    
    # Call it with a byte string and get an error since `.argtypes` is defined.
    dll.PlaySoundW(b'sound.wav',None,SND_FILENAME)
    

    Output (after sound plays):

    Traceback (most recent call last):
      File "C:\test.py", line 15, in <module>
        dll.PlaySoundW(b'sound.wav',None,SND_FILENAME)
    ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
    

    Or skip all that work and just use the winsound module:

    import winsound
    winsound.PlaySound('sound.wav',winsound.SND_FILENAME)