import os import struct import sys import unittest import pythoncom import pywintypes import win32api import win32com.directsound.directsound as ds import win32event from pywin32_testutil import TestSkipped, find_test_fixture # next two lines are for for debugging: # import win32com # import directsound as ds WAV_FORMAT_PCM = 1 WAV_HEADER_SIZE = struct.calcsize("<4sl4s4slhhllhh4sl") def wav_header_unpack(data): ( riff, riffsize, wave, fmt, fmtsize, format, nchannels, samplespersecond, datarate, blockalign, bitspersample, data, datalength, ) = struct.unpack("<4sl4s4slhhllhh4sl", data) if riff != b"RIFF": raise ValueError("invalid wav header") if fmtsize != 16 or fmt != b"fmt " or data != b"data": # fmt chuck is not first chunk, directly followed by data chuck # It is nowhere required that they are, it is just very common raise ValueError("cannot understand wav header") wfx = pywintypes.WAVEFORMATEX() wfx.wFormatTag = format wfx.nChannels = nchannels wfx.nSamplesPerSec = samplespersecond wfx.nAvgBytesPerSec = datarate wfx.nBlockAlign = blockalign wfx.wBitsPerSample = bitspersample return wfx, datalength def wav_header_pack(wfx, datasize): return struct.pack( "<4sl4s4slhhllhh4sl", b"RIFF", 36 + datasize, b"WAVE", b"fmt ", 16, wfx.wFormatTag, wfx.nChannels, wfx.nSamplesPerSec, wfx.nAvgBytesPerSec, wfx.nBlockAlign, wfx.wBitsPerSample, b"data", datasize, ) class WAVEFORMATTest(unittest.TestCase): def test_1_Type(self): "WAVEFORMATEX type" w = pywintypes.WAVEFORMATEX() self.assertTrue(type(w) == pywintypes.WAVEFORMATEXType) def test_2_Attr(self): "WAVEFORMATEX attribute access" # A wav header for a soundfile from a CD should look like this... w = pywintypes.WAVEFORMATEX() w.wFormatTag = pywintypes.WAVE_FORMAT_PCM w.nChannels = 2 w.nSamplesPerSec = 44100 w.nAvgBytesPerSec = 176400 w.nBlockAlign = 4 w.wBitsPerSample = 16 self.assertTrue(w.wFormatTag == 1) self.assertTrue(w.nChannels == 2) self.assertTrue(w.nSamplesPerSec == 44100) self.assertTrue(w.nAvgBytesPerSec == 176400) self.assertTrue(w.nBlockAlign == 4) self.assertTrue(w.wBitsPerSample == 16) class DSCAPSTest(unittest.TestCase): def test_1_Type(self): "DSCAPS type" c = ds.DSCAPS() self.assertTrue(type(c) == ds.DSCAPSType) def test_2_Attr(self): "DSCAPS attribute access" c = ds.DSCAPS() c.dwFlags = 1 c.dwMinSecondarySampleRate = 2 c.dwMaxSecondarySampleRate = 3 c.dwPrimaryBuffers = 4 c.dwMaxHwMixingAllBuffers = 5 c.dwMaxHwMixingStaticBuffers = 6 c.dwMaxHwMixingStreamingBuffers = 7 c.dwFreeHwMixingAllBuffers = 8 c.dwFreeHwMixingStaticBuffers = 9 c.dwFreeHwMixingStreamingBuffers = 10 c.dwMaxHw3DAllBuffers = 11 c.dwMaxHw3DStaticBuffers = 12 c.dwMaxHw3DStreamingBuffers = 13 c.dwFreeHw3DAllBuffers = 14 c.dwFreeHw3DStaticBuffers = 15 c.dwFreeHw3DStreamingBuffers = 16 c.dwTotalHwMemBytes = 17 c.dwFreeHwMemBytes = 18 c.dwMaxContigFreeHwMemBytes = 19 c.dwUnlockTransferRateHwBuffers = 20 c.dwPlayCpuOverheadSwBuffers = 21 self.assertTrue(c.dwFlags == 1) self.assertTrue(c.dwMinSecondarySampleRate == 2) self.assertTrue(c.dwMaxSecondarySampleRate == 3) self.assertTrue(c.dwPrimaryBuffers == 4) self.assertTrue(c.dwMaxHwMixingAllBuffers == 5) self.assertTrue(c.dwMaxHwMixingStaticBuffers == 6) self.assertTrue(c.dwMaxHwMixingStreamingBuffers == 7) self.assertTrue(c.dwFreeHwMixingAllBuffers == 8) self.assertTrue(c.dwFreeHwMixingStaticBuffers == 9) self.assertTrue(c.dwFreeHwMixingStreamingBuffers == 10) self.assertTrue(c.dwMaxHw3DAllBuffers == 11) self.assertTrue(c.dwMaxHw3DStaticBuffers == 12) self.assertTrue(c.dwMaxHw3DStreamingBuffers == 13) self.assertTrue(c.dwFreeHw3DAllBuffers == 14) self.assertTrue(c.dwFreeHw3DStaticBuffers == 15) self.assertTrue(c.dwFreeHw3DStreamingBuffers == 16) self.assertTrue(c.dwTotalHwMemBytes == 17) self.assertTrue(c.dwFreeHwMemBytes == 18) self.assertTrue(c.dwMaxContigFreeHwMemBytes == 19) self.assertTrue(c.dwUnlockTransferRateHwBuffers == 20) self.assertTrue(c.dwPlayCpuOverheadSwBuffers == 21) class DSBCAPSTest(unittest.TestCase): def test_1_Type(self): "DSBCAPS type" c = ds.DSBCAPS() self.assertTrue(type(c) == ds.DSBCAPSType) def test_2_Attr(self): "DSBCAPS attribute access" c = ds.DSBCAPS() c.dwFlags = 1 c.dwBufferBytes = 2 c.dwUnlockTransferRate = 3 c.dwPlayCpuOverhead = 4 self.assertTrue(c.dwFlags == 1) self.assertTrue(c.dwBufferBytes == 2) self.assertTrue(c.dwUnlockTransferRate == 3) self.assertTrue(c.dwPlayCpuOverhead == 4) class DSCCAPSTest(unittest.TestCase): def test_1_Type(self): "DSCCAPS type" c = ds.DSCCAPS() self.assertTrue(type(c) == ds.DSCCAPSType) def test_2_Attr(self): "DSCCAPS attribute access" c = ds.DSCCAPS() c.dwFlags = 1 c.dwFormats = 2 c.dwChannels = 4 self.assertTrue(c.dwFlags == 1) self.assertTrue(c.dwFormats == 2) self.assertTrue(c.dwChannels == 4) class DSCBCAPSTest(unittest.TestCase): def test_1_Type(self): "DSCBCAPS type" c = ds.DSCBCAPS() self.assertTrue(type(c) == ds.DSCBCAPSType) def test_2_Attr(self): "DSCBCAPS attribute access" c = ds.DSCBCAPS() c.dwFlags = 1 c.dwBufferBytes = 2 self.assertTrue(c.dwFlags == 1) self.assertTrue(c.dwBufferBytes == 2) class DSBUFFERDESCTest(unittest.TestCase): def test_1_Type(self): "DSBUFFERDESC type" c = ds.DSBUFFERDESC() self.assertTrue(type(c) == ds.DSBUFFERDESCType) def test_2_Attr(self): "DSBUFFERDESC attribute access" c = ds.DSBUFFERDESC() c.dwFlags = 1 c.dwBufferBytes = 2 c.lpwfxFormat = pywintypes.WAVEFORMATEX() c.lpwfxFormat.wFormatTag = pywintypes.WAVE_FORMAT_PCM c.lpwfxFormat.nChannels = 2 c.lpwfxFormat.nSamplesPerSec = 44100 c.lpwfxFormat.nAvgBytesPerSec = 176400 c.lpwfxFormat.nBlockAlign = 4 c.lpwfxFormat.wBitsPerSample = 16 self.assertTrue(c.dwFlags == 1) self.assertTrue(c.dwBufferBytes == 2) self.assertTrue(c.lpwfxFormat.wFormatTag == 1) self.assertTrue(c.lpwfxFormat.nChannels == 2) self.assertTrue(c.lpwfxFormat.nSamplesPerSec == 44100) self.assertTrue(c.lpwfxFormat.nAvgBytesPerSec == 176400) self.assertTrue(c.lpwfxFormat.nBlockAlign == 4) self.assertTrue(c.lpwfxFormat.wBitsPerSample == 16) def invalid_format(self, c): c.lpwfxFormat = 17 def test_3_invalid_format(self): "DSBUFFERDESC invalid lpwfxFormat assignment" c = ds.DSBUFFERDESC() self.assertRaises(ValueError, self.invalid_format, c) class DSCBUFFERDESCTest(unittest.TestCase): def test_1_Type(self): "DSCBUFFERDESC type" c = ds.DSCBUFFERDESC() self.assertTrue(type(c) == ds.DSCBUFFERDESCType) def test_2_Attr(self): "DSCBUFFERDESC attribute access" c = ds.DSCBUFFERDESC() c.dwFlags = 1 c.dwBufferBytes = 2 c.lpwfxFormat = pywintypes.WAVEFORMATEX() c.lpwfxFormat.wFormatTag = pywintypes.WAVE_FORMAT_PCM c.lpwfxFormat.nChannels = 2 c.lpwfxFormat.nSamplesPerSec = 44100 c.lpwfxFormat.nAvgBytesPerSec = 176400 c.lpwfxFormat.nBlockAlign = 4 c.lpwfxFormat.wBitsPerSample = 16 self.assertTrue(c.dwFlags == 1) self.assertTrue(c.dwBufferBytes == 2) self.assertTrue(c.lpwfxFormat.wFormatTag == 1) self.assertTrue(c.lpwfxFormat.nChannels == 2) self.assertTrue(c.lpwfxFormat.nSamplesPerSec == 44100) self.assertTrue(c.lpwfxFormat.nAvgBytesPerSec == 176400) self.assertTrue(c.lpwfxFormat.nBlockAlign == 4) self.assertTrue(c.lpwfxFormat.wBitsPerSample == 16) def invalid_format(self, c): c.lpwfxFormat = 17 def test_3_invalid_format(self): "DSCBUFFERDESC invalid lpwfxFormat assignment" c = ds.DSCBUFFERDESC() self.assertRaises(ValueError, self.invalid_format, c) class DirectSoundTest(unittest.TestCase): # basic tests - mostly just exercise the functions def testEnumerate(self): """DirectSoundEnumerate() sanity tests""" devices = ds.DirectSoundEnumerate() # this might fail on machines without a sound card self.assertTrue(len(devices)) # if we have an entry, it must be a tuple of size 3 self.assertTrue(len(devices[0]) == 3) def testCreate(self): """DirectSoundCreate()""" try: d = ds.DirectSoundCreate(None, None) except pythoncom.com_error as exc: if exc.hresult != ds.DSERR_NODRIVER: raise raise TestSkipped(exc) def testPlay(self): """Mesdames et Messieurs, la cour de Devin Dazzle""" # relative to 'testall.py' in the win32com test suite. extra = os.path.join( os.path.dirname(sys.argv[0]), "../../win32comext/directsound/test" ) fname = find_test_fixture("01-Intro.wav", extra) with open(fname, "rb") as f: hdr = f.read(WAV_HEADER_SIZE) wfx, size = wav_header_unpack(hdr) try: d = ds.DirectSoundCreate(None, None) except pythoncom.com_error as exc: if exc.hresult != ds.DSERR_NODRIVER: raise raise TestSkipped(exc) d.SetCooperativeLevel(None, ds.DSSCL_PRIORITY) sdesc = ds.DSBUFFERDESC() sdesc.dwFlags = ds.DSBCAPS_STICKYFOCUS | ds.DSBCAPS_CTRLPOSITIONNOTIFY sdesc.dwBufferBytes = size sdesc.lpwfxFormat = wfx buffer = d.CreateSoundBuffer(sdesc, None) event = win32event.CreateEvent(None, 0, 0, None) notify = buffer.QueryInterface(ds.IID_IDirectSoundNotify) notify.SetNotificationPositions((ds.DSBPN_OFFSETSTOP, event)) buffer.Update(0, f.read(size)) buffer.Play(0) win32event.WaitForSingleObject(event, -1) class DirectSoundCaptureTest(unittest.TestCase): # basic tests - mostly just exercise the functions def testEnumerate(self): """DirectSoundCaptureEnumerate() sanity tests""" devices = ds.DirectSoundCaptureEnumerate() # this might fail on machines without a sound card self.assertTrue(len(devices)) # if we have an entry, it must be a tuple of size 3 self.assertTrue(len(devices[0]) == 3) def testCreate(self): """DirectSoundCreate()""" try: d = ds.DirectSoundCaptureCreate(None, None) except pythoncom.com_error as exc: if exc.hresult != ds.DSERR_NODRIVER: raise raise TestSkipped(exc) def testRecord(self): try: d = ds.DirectSoundCaptureCreate(None, None) except pythoncom.com_error as exc: if exc.hresult != ds.DSERR_NODRIVER: raise raise TestSkipped(exc) sdesc = ds.DSCBUFFERDESC() sdesc.dwBufferBytes = 352800 # 2 seconds sdesc.lpwfxFormat = pywintypes.WAVEFORMATEX() sdesc.lpwfxFormat.wFormatTag = pywintypes.WAVE_FORMAT_PCM sdesc.lpwfxFormat.nChannels = 2 sdesc.lpwfxFormat.nSamplesPerSec = 44100 sdesc.lpwfxFormat.nAvgBytesPerSec = 176400 sdesc.lpwfxFormat.nBlockAlign = 4 sdesc.lpwfxFormat.wBitsPerSample = 16 buffer = d.CreateCaptureBuffer(sdesc) event = win32event.CreateEvent(None, 0, 0, None) notify = buffer.QueryInterface(ds.IID_IDirectSoundNotify) notify.SetNotificationPositions((ds.DSBPN_OFFSETSTOP, event)) buffer.Start(0) win32event.WaitForSingleObject(event, -1) event.Close() data = buffer.Update(0, 352800) fname = os.path.join(win32api.GetTempPath(), "test_directsound_record.wav") f = open(fname, "wb") f.write(wav_header_pack(sdesc.lpwfxFormat, 352800)) f.write(data) f.close() if __name__ == "__main__": unittest.main()