Skip to content

Commit

Permalink
Merge branch 'release/v20160929'
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreMiras committed Sep 29, 2016
2 parents 83c039a + 691024c commit 08df87f
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 19 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## [20160929]
- Fixed crash on print AudioDevice & AudioSession on Python3
- Fixed GetAllSessions() reliability, refs #1

## [20160508]
- Fixed enum requirement
- Unit tested examples:
Expand Down
30 changes: 13 additions & 17 deletions pycaw/pycaw.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
"""
Python wrapper around the Core Audio Windows API.
"""
from future.utils import python_2_unicode_compatible
import psutil
import comtypes
from enum import Enum
from ctypes import cast, HRESULT, POINTER, Structure, Union, \
from ctypes import HRESULT, POINTER, Structure, Union, \
c_uint32, c_longlong, c_float
from ctypes.wintypes import BOOL, VARIANT_BOOL, WORD, DWORD, \
UINT, INT, LONG, ULARGE_INTEGER, LPWSTR, LPCWSTR
from comtypes import IUnknown, GUID, COMMETHOD
from comtypes.automation import VARTYPE, VT_BOOL, VT_LPWSTR, VT_UI4, VT_CLSID


IID_Empty = GUID(
'{00000000-0000-0000-0000-000000000000}')

Expand Down Expand Up @@ -55,7 +57,7 @@ def GetValue(self):
# return (Guid)Marshal.PtrToStructure(union.puuid, typeof(Guid))
return
else:
return unicode(vt) + u":?"
return "%s:?" % (vt)


class WAVEFORMATEX(Structure):
Expand Down Expand Up @@ -383,17 +385,15 @@ class IAudioClient(IUnknown):
(['out'], POINTER(POINTER(IUnknown)), 'ppv')))


@python_2_unicode_compatible
class PROPERTYKEY(Structure):
_fields_ = [
('fmtid', GUID),
('pid', DWORD),
]

def __unicode__(self):
return unicode(self.fmtid) + u" " + unicode(self.pid)

def __str__(self):
return unicode(self).encode('utf-8')
return "%s %s" % (self.fmtid, self.pid)


class IPropertyStore(IUnknown):
Expand Down Expand Up @@ -497,6 +497,7 @@ class IMMDeviceEnumerator(IUnknown):
COMMETHOD([], HRESULT, 'NotImpl2'))


@python_2_unicode_compatible
class AudioDevice(object):
"""
http://stackoverflow.com/a/20982715/185510
Expand All @@ -507,10 +508,7 @@ def __init__(self, id, state, properties):
self.properties = properties

def __str__(self):
return unicode(self).encode('utf-8')

def __unicode__(self):
return u"AudioDevice: " + unicode(self.FriendlyName)
return "AudioDevice: %s" % (self.FriendlyName)

@property
def FriendlyName(self):
Expand All @@ -520,6 +518,7 @@ def FriendlyName(self):
return value


@python_2_unicode_compatible
class AudioSession(object):
"""
http://stackoverflow.com/a/20982715/185510
Expand All @@ -531,15 +530,12 @@ def __init__(self, audio_session_control2):
self._volume = None

def __str__(self):
return unicode(self).encode('utf-8')

def __unicode__(self):
s = self.DisplayName
if s:
return "DisplayName: " + s
if self.Process is not None:
return "Process: " + self.Process.name()
return "Pid: " + self.ProcessId
return "Pid: %s" % (self.ProcessId)

@property
def Process(self):
Expand Down Expand Up @@ -633,7 +629,7 @@ def GetAudioSessionManager():
# win7+ only
o = speakers.Activate(
IAudioSessionManager2._iid_, comtypes.CLSCTX_ALL, None)
mgr = cast(o, POINTER(IAudioSessionManager2))
mgr = o.QueryInterface(IAudioSessionManager2)
return mgr

@staticmethod
Expand All @@ -648,7 +644,7 @@ def GetAllSessions():
ctl = sessionEnumerator.GetSession(i)
if ctl is None:
continue
ctl2 = cast(ctl, POINTER(IAudioSessionControl2))
ctl2 = ctl.QueryInterface(IAudioSessionControl2)
if ctl2 is not None:
audio_session = AudioSession(ctl2)
audio_sessions.append(audio_session)
Expand Down Expand Up @@ -678,7 +674,7 @@ def CreateDevice(dev):
v = value.GetValue()
# TODO
# PropVariantClear(byref(value))
name = unicode(pk)
name = str(pk)
properties[name] = v
audioState = AudioDeviceState(state)
return AudioDevice(id, audioState, properties)
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
comtypes==1.1.2
enum34==1.1.4
psutil==4.1.0
future==0.15.2
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
from distutils.core import setup

setup(name='pycaw',
version='20160508',
version='20160929',
description='Python Core Audio Windows Library',
author='Andre Miras',
url='https://github.com/AndreMiras/pycaw',
packages=['pycaw'],
install_requires=['comtypes', 'enum34', 'psutil'])
install_requires=['comtypes', 'enum34', 'psutil', 'future'])
60 changes: 60 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Verifies core features run as expected.
"""
from __future__ import print_function
import sys
import unittest
from contextlib import contextmanager
from pycaw.pycaw import AudioUtilities
try:
from StringIO import StringIO
except ImportError:
from io import StringIO


@contextmanager
def captured_output():
new_out, new_err = StringIO(), StringIO()
old_out, old_err = sys.stdout, sys.stderr
try:
sys.stdout, sys.stderr = new_out, new_err
yield sys.stdout, sys.stderr
finally:
sys.stdout, sys.stderr = old_out, old_err


class TestCore(unittest.TestCase):

def test_session_unicode(self):
"""
Makes sure printing a session doesn't crash.
"""
with captured_output() as (out, err):
sessions = AudioUtilities.GetAllSessions()
print("sessions: %s" % sessions)
for session in sessions:
print("session: %s" % session)
print("session.Process: %s" % session.Process)

def test_device_unicode(self):
"""
Makes sure printing a device doesn't crash.
"""
with captured_output() as (out, err):
devices = AudioUtilities.GetAllDevices()
print("devices: %s" % devices)
for device in devices:
print("device: %s" % device)

def test_getallsessions_reliability(self):
"""
Verifies AudioUtilities.GetAllSessions() is reliable
even calling it multiple times, refs:
https://github.com/AndreMiras/pycaw/issues/1
"""
for _ in range(100):
sessions = AudioUtilities.GetAllSessions()
self.assertTrue(len(sessions) > 0)

if __name__ == '__main__':
unittest.main()

0 comments on commit 08df87f

Please sign in to comment.