import unittest import struct import sys import os import pywintypes import win32event, win32api import os from pywin32_testutil import TestSkipped import win32com.directsound.directsound as ds import pythoncom # 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''' # look for the test file in various places candidates = [ os.path.dirname(__file__), os.path.dirname(sys.argv[0]), # relative to 'testall.py' in the win32com test suite. os.path.join(os.path.dirname(sys.argv[0]), '../../win32comext/directsound/test'), '.', ] for candidate in candidates: fname=os.path.join(candidate, "01-Intro.wav") if os.path.isfile(fname): break else: raise TestSkipped("Can't find test .wav file to play") with open(fname, 'rb') as f: hdr = f.read(WAV_HEADER_SIZE) wfx, size = wav_header_unpack(hdr) d = ds.DirectSoundCreate(None, None) 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()