def __init__(self): self._dsound = lib.IDirectSound() lib.DirectSoundCreate(None, ctypes.byref(self._dsound), None) # A trick used by mplayer.. use desktop as window handle since it # would be complex to use pyglet window handles (and what to do when # application is audio only?). hwnd = _user32.GetDesktopWindow() self._dsound.SetCooperativeLevel(hwnd, lib.DSSCL_NORMAL) # Create primary buffer with 3D and volume capabilities self._buffer = lib.IDirectSoundBuffer() dsbd = lib.DSBUFFERDESC() dsbd.dwSize = ctypes.sizeof(dsbd) dsbd.dwFlags = (lib.DSBCAPS_CTRL3D | lib.DSBCAPS_CTRLVOLUME | lib.DSBCAPS_PRIMARYBUFFER) self._dsound.CreateSoundBuffer(dsbd, ctypes.byref(self._buffer), None) # Create listener self._listener = lib.IDirectSound3DListener() self._buffer.QueryInterface(lib.IID_IDirectSound3DListener, ctypes.byref(self._listener)) # Create worker thread self.worker = DirectSoundWorker() self.worker.start()
def __init__(self, source_group, player): super(DirectSoundAudioPlayer, self).__init__(source_group, player) # Locking strategy: # All DirectSound calls should be locked. All instance vars relating # to buffering/filling/time/events should be locked (used by both # application and worker thread). Other instance vars (consts and # 3d vars) do not need to be locked. self._lock = threading.RLock() # Desired play state (may be actually paused due to underrun -- not # implemented yet). self._playing = False # Up to one audio data may be buffered if too much data was received # from the source that could not be written immediately into the # buffer. See refill(). self._next_audio_data = None # Theoretical write and play cursors for an infinite buffer. play # cursor is always <= write cursor (when equal, underrun is # happening). self._write_cursor = 0 self._play_cursor = 0 # Cursor position of end of data. Silence is written after # eos for one buffer size. self._eos_cursor = None # Indexes into DSound circular buffer. Complications ensue wrt each # other to avoid writing over the play cursor. See get_write_size and # write(). self._play_cursor_ring = 0 self._write_cursor_ring = 0 # List of (play_cursor, MediaEvent), in sort order self._events = [] # List of (cursor, timestamp), in sort order (cursor gives expiry # place of the timestamp) self._timestamps = [] audio_format = source_group.audio_format wfx = lib.WAVEFORMATEX() wfx.wFormatTag = lib.WAVE_FORMAT_PCM wfx.nChannels = audio_format.channels wfx.nSamplesPerSec = audio_format.sample_rate wfx.wBitsPerSample = audio_format.sample_size wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels // 8 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign dsbdesc = lib.DSBUFFERDESC() dsbdesc.dwSize = ctypes.sizeof(dsbdesc) dsbdesc.dwFlags = (lib.DSBCAPS_GLOBALFOCUS | lib.DSBCAPS_GETCURRENTPOSITION2 | lib.DSBCAPS_CTRLFREQUENCY | lib.DSBCAPS_CTRLVOLUME) if audio_format.channels == 1: dsbdesc.dwFlags |= lib.DSBCAPS_CTRL3D dsbdesc.dwBufferBytes = self._buffer_size dsbdesc.lpwfxFormat = ctypes.pointer(wfx) # DSound buffer self._buffer = lib.IDirectSoundBuffer() driver._dsound.CreateSoundBuffer(dsbdesc, ctypes.byref(self._buffer), None) if audio_format.channels == 1: self._buffer3d = lib.IDirectSound3DBuffer() self._buffer.QueryInterface(lib.IID_IDirectSound3DBuffer, ctypes.byref(self._buffer3d)) else: self._buffer3d = None self._buffer.SetCurrentPosition(0) self.refill(self._buffer_size)