pythoncdllctypeswinmm

Winmm.dll doesn't return length of file


I am trying to play a .mp3 file in python using winmm.dll (ctypes.windll.winmm). But when I try to get a length of some file in milliseconds, instead of actual length (05:23 = about 323000 ms) I get just 3. Time format got by status command is "m", and it doesn't change after set command. Here is some code that illustrates the problem:

from ctypes import windll, c_buffer

fp = 'song.mp3'
alias = 'test'

buf = c_buffer(255)
r = windll.winmm.mciSendStringW(f'open "{fp}" alias {alias}', buf, 254, 0)
print(r)

buf = c_buffer(255)
r = windll.winmm.mciSendStringW(f'status {alias} time format', buf, 254, 0)
print(r, buf.value)

buf = c_buffer(255)
r = windll.winmm.mciSendStringW(f'set {alias} time format milliseconds', buf, 254, 0)
print(r)

buf = c_buffer(255)
r = windll.winmm.mciSendStringW(f'status {alias} time format', buf, 254, 0)
print(r, buf.value)

buf = c_buffer(255)
r = windll.winmm.mciSendStringW(f'status {alias} length', buf, 254, 0)
print(r, buf.value)

And its output:

0
0 b'm'
0
0 b'm'
0 b'3'

Thanks in advance for your help!


Solution

  • Listing [Python.Docs]: ctypes - A foreign function library for Python.

    As I specified in my comment this is a classic Undefined Behavior example. Check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for a common pitfall when working with CTypes (calling functions).
    Also, you're mixing 08bit and 16bit strings.

    code00.py:

    #!/usr/bin/env python
    
    import ctypes as cts
    import sys
    import time
    from ctypes import wintypes as wts
    
    
    def main(*argv):
        winmm = cts.WinDLL("Winmm.dll")
        mciSendString  = winmm.mciSendStringW
        mciSendString.argtypes = (wts.LPCWSTR, wts.LPWSTR, wts.UINT, wts.HANDLE)
        mciSendString.restype = wts.DWORD
    
        file_name = "./SampleAudio_0.4mb.mp3"  # Downloaded from https://www.sample-videos.com/audio/mp3/crowd-cheering.mp3
        alias_name = "test00"
        status_commands = [
            f"status {alias_name} time format",
            f"status {alias_name} length",
        ]
    
        commands = [f"open \"{file_name}\" alias {alias_name}"]
        commands.extend(status_commands)
        commands.append(f"set {alias_name} time format ms")
        commands.extend(status_commands)
        #commands.append(f"set {alias_name} speed 2000")
        #commands.append(f"play {alias_name} wait")
        commands.append(f"close {alias_name}")
    
        out_buf_len = 0xFF
        out_buf = cts.create_unicode_buffer(out_buf_len)
        for idx, command in enumerate(commands):
            print(f"Sending {command}...")
            res = mciSendString(command, out_buf, out_buf_len, None)
            print(f"  Result: {res}\n  Output: {out_buf.value}")
            #if idx == len(commands) - 2:
            #    time.sleep(1)
    
    
    if __name__ == "__main__":
        print(
            "Python {:s} {:03d}bit on {:s}\n".format(
                " ".join(elem.strip() for elem in sys.version.split("\n")),
                64 if sys.maxsize > 0x100000000 else 32,
                sys.platform
            )
        )
        rc = main(*sys.argv[1:])
        print("\nDone.\n")
        sys.exit(rc)
    

    Output:

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q059343461]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 064bit on win32
    
    Sending open "./SampleAudio_0.4mb.mp3" alias test00...
      Result: 0
      Output: 1
    Sending status test00 time format...
      Result: 0
      Output: milliseconds
    Sending status test00 length...
      Result: 0
      Output: 27746
    Sending set test00 time format ms...
      Result: 0
      Output:
    Sending status test00 time format...
      Result: 0
      Output: milliseconds
    Sending status test00 length...
      Result: 0
      Output: 27746
    Sending close test00...
      Result: 0
      Output:
    
    Done.