Skip to content

Commit

Permalink
Merge branch 'release/v20181226'
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreMiras committed Dec 26, 2018
2 parents 08df87f + 93c167e commit c240c17
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 22 deletions.
11 changes: 11 additions & 0 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Make sure to give enough information to help debugging your issue.
Here are some hints:

- current requirements (pip freeze)
- OS version
- architecture
- Python version
- pycaw version
- project files
- exact stacktrace / accurate error message
- and so on
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Custom
venv/
*.swp

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
19 changes: 19 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
os: windows

language: shell

before_install:
- choco install python3 --version 3.6.7
- export PATH="/c/Python36:/c/Python36/Scripts:$PATH"

install:
- python -m pip install -r requirements.txt
- python -m pip install tox-travis

script:
- tox -e pep8
- tox -e isort
# can't run actual unit tests on Travis
# https://travis-ci.org/AndreMiras/pycaw/builds/472124432
# _ctypes.COMError: (-2147023174, 'The RPC server is unavailable.', (None, None, None, 0, None))
# - tox -e py36
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## [20181226]
- GetVolumeStepInfo() interface fixes
- IAudioEndpointVolumeCallback::OnNotify support, refs #10
- Setup (limited) continuous testing, refs #12

## [20160929]
- Fixed crash on print AudioDevice & AudioSession on Python3
- Fixed GetAllSessions() reliability, refs #1
Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
# pycaw

[![Build Status](https://travis-ci.org/AndreMiras/pycaw.svg?branch=develop)](https://travis-ci.org/AndreMiras/pycaw)

Python Core Audio Windows Library, working for both Python2 and Python3.

## Install

Latest stable release:

pip install https://github.com/AndreMiras/pycaw/archive/master.zip
```bash
pip install https://github.com/AndreMiras/pycaw/archive/master.zip
```

Development branch:
```bash
pip install https://github.com/AndreMiras/pycaw/archive/develop.zip
```

pip install https://github.com/AndreMiras/pycaw/archive/develop.zip
System requirements:
```bash
choco install visualcppbuildtools
```

## Usage

Expand Down
80 changes: 80 additions & 0 deletions examples/audio_controller_class_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
Per session GetMute() SetMute() GetMasterVolume() SetMasterVolume() using
SimpleAudioVolume.
"""
from __future__ import print_function

from pycaw.pycaw import AudioUtilities


class AudioController(object):
def __init__(self, process_name):
self.process_name = process_name
self.volume = self.process_volume()

def mute(self):
sessions = AudioUtilities.GetAllSessions()
for session in sessions:
interface = session.SimpleAudioVolume
if session.Process and session.Process.name() == self.process_name:
interface.SetMute(1, None)
print(self.process_name, 'has been muted.') # debug

def unmute(self):
sessions = AudioUtilities.GetAllSessions()
for session in sessions:
interface = session.SimpleAudioVolume
if session.Process and session.Process.name() == self.process_name:
interface.SetMute(0, None)
print(self.process_name, 'has been unmuted.') # debug

def process_volume(self):
sessions = AudioUtilities.GetAllSessions()
for session in sessions:
interface = session.SimpleAudioVolume
if session.Process and session.Process.name() == self.process_name:
print('Volume:', interface.GetMasterVolume()) # debug
return interface.GetMasterVolume()

def set_volume(self, decibels):
sessions = AudioUtilities.GetAllSessions()
for session in sessions:
interface = session.SimpleAudioVolume
if session.Process and session.Process.name() == self.process_name:
# only set volume in the range 0.0 to 1.0
self.volume = min(1.0, max(0.0, decibels))
interface.SetMasterVolume(self.volume, None)
print('Volume set to', self.volume) # debug

def decrease_volume(self, decibels):
sessions = AudioUtilities.GetAllSessions()
for session in sessions:
interface = session.SimpleAudioVolume
if session.Process and session.Process.name() == self.process_name:
# 0.0 is the min value, reduce by decibels
self.volume = max(0.0, self.volume-decibels)
interface.SetMasterVolume(self.volume, None)
print('Volume reduced to', self.volume) # debug

def increase_volume(self, decibels):
sessions = AudioUtilities.GetAllSessions()
for session in sessions:
interface = session.SimpleAudioVolume
if session.Process and session.Process.name() == self.process_name:
# 1.0 is the max value, raise by decibels
self.volume = min(1.0, self.volume+decibels)
interface.SetMasterVolume(self.volume, None)
print('Volume raised to', self.volume) # debug


def main():
audio_controller = AudioController('chrome.exe')
audio_controller.set_volume(1.0)
audio_controller.mute()
audio_controller.decrease_volume(0.25)
audio_controller.increase_volume(0.05)
audio_controller.unmute()


if __name__ == "__main__":
main()
5 changes: 4 additions & 1 deletion examples/audio_endpoint_volume_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
Get and set access to master volume example.
"""
from __future__ import print_function
from ctypes import cast, POINTER

from ctypes import POINTER, cast

from comtypes import CLSCTX_ALL

from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume


Expand Down
1 change: 1 addition & 0 deletions examples/simple_audio_volume_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Per session GetMute() SetMute() using ISimpleAudioVolume.
"""
from __future__ import print_function

from pycaw.pycaw import AudioUtilities, ISimpleAudioVolume


Expand Down
35 changes: 35 additions & 0 deletions examples/volume_callback_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
IAudioEndpointVolumeCallback.OnNotify() example.
The OnNotify() callback method gets called on volume change.
"""
from __future__ import print_function

from ctypes import POINTER, cast

from comtypes import CLSCTX_ALL, COMObject

from pycaw.pycaw import (AudioUtilities, IAudioEndpointVolume,
IAudioEndpointVolumeCallback)


class AudioEndpointVolumeCallback(COMObject):
_com_interfaces_ = [IAudioEndpointVolumeCallback]

def OnNotify(self, pNotify):
print('OnNotify callback')


def main():
devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(
IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = cast(interface, POINTER(IAudioEndpointVolume))
callback = AudioEndpointVolumeCallback()
volume.RegisterControlChangeNotify(callback)
for i in range(3):
volume.SetMute(0, None)
volume.SetMute(1, None)


if __name__ == "__main__":
main()
59 changes: 45 additions & 14 deletions pycaw/pycaw.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
"""
Python wrapper around the Core Audio Windows API.
"""
from future.utils import python_2_unicode_compatible
import psutil
import comtypes
from ctypes import (HRESULT, POINTER, Structure, Union, c_float, c_longlong,
c_uint32)
from ctypes.wintypes import (BOOL, DWORD, INT, LONG, LPCWSTR, LPWSTR, UINT,
ULARGE_INTEGER, VARIANT_BOOL, WORD)
from enum import Enum
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

import comtypes
import psutil
from comtypes import COMMETHOD, GUID, IUnknown
from comtypes.automation import VARTYPE, VT_BOOL, VT_CLSID, VT_LPWSTR, VT_UI4
from future.utils import python_2_unicode_compatible

IID_Empty = GUID(
'{00000000-0000-0000-0000-000000000000}')
Expand Down Expand Up @@ -110,15 +110,46 @@ class AUDCLNT_SHAREMODE(Enum):
AUDCLNT_SHAREMODE_EXCLUSIVE = 0x00000002


class AUDIO_VOLUME_NOTIFICATION_DATA(Structure):
_fields_ = [
('guidEventContext', GUID),
('bMuted', BOOL),
('fMasterVolume', c_float),
('nChannels', UINT),
('afChannelVolumes', c_float * 8),
]


PAUDIO_VOLUME_NOTIFICATION_DATA = POINTER(AUDIO_VOLUME_NOTIFICATION_DATA)


class IAudioEndpointVolumeCallback(IUnknown):
_iid_ = GUID('{b1136c83-b6b5-4add-98a5-a2df8eedf6fa}')
_methods_ = (
# HRESULT OnNotify(
# [in] PAUDIO_VOLUME_NOTIFICATION_DATA pNotify);
COMMETHOD([], HRESULT, 'OnNotify',
(['in'],
PAUDIO_VOLUME_NOTIFICATION_DATA,
'pNotify')),
)


class IAudioEndpointVolume(IUnknown):
_iid_ = GUID('{5CDF2C82-841E-4546-9722-0CF74078229A}')
_methods_ = (
# HRESULT RegisterControlChangeNotify(
# [in] IAudioEndpointVolumeCallback *pNotify);
COMMETHOD([], HRESULT, 'NotImpl1'),
COMMETHOD([], HRESULT, 'RegisterControlChangeNotify',
(['in'],
POINTER(IAudioEndpointVolumeCallback),
'pNotify')),
# HRESULT UnregisterControlChangeNotify(
# [in] IAudioEndpointVolumeCallback *pNotify);
COMMETHOD([], HRESULT, 'NotImpl2'),
COMMETHOD([], HRESULT, 'UnregisterControlChangeNotify',
(['in'],
POINTER(IAudioEndpointVolumeCallback),
'pNotify')),
# HRESULT GetChannelCount([out] UINT *pnChannelCount);
COMMETHOD([], HRESULT, 'GetChannelCount',
(['out'], POINTER(UINT), 'pnChannelCount')),
Expand All @@ -130,7 +161,7 @@ class IAudioEndpointVolume(IUnknown):
# HRESULT SetMasterVolumeLevelScalar(
# [in] float fLevel, [in] LPCGUID pguidEventContext);
COMMETHOD([], HRESULT, 'SetMasterVolumeLevelScalar',
(['in'], c_float, 'fLevelDB'),
(['in'], c_float, 'fLevel'),
(['in'], POINTER(GUID), 'pguidEventContext')),
# HRESULT GetMasterVolumeLevel([out] float *pfLevelDB);
COMMETHOD([], HRESULT, 'GetMasterVolumeLevel',
Expand Down Expand Up @@ -177,8 +208,8 @@ class IAudioEndpointVolume(IUnknown):
# [out] UINT *pnStep,
# [out] UINT *pnStepCount);
COMMETHOD([], HRESULT, 'GetVolumeStepInfo',
(['out'], POINTER(c_float), 'pnStep'),
(['out'], POINTER(c_float), 'pnStepCount')),
(['out'], POINTER(DWORD), 'pnStep'),
(['out'], POINTER(DWORD), 'pnStepCount')),
# HRESULT VolumeStepUp([in] LPCGUID pguidEventContext);
COMMETHOD([], HRESULT, 'VolumeStepUp',
(['in'], POINTER(GUID), 'pguidEventContext')),
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
comtypes==1.1.2
comtypes==1.1.3.post2
enum34==1.1.4
psutil==4.1.0
psutil==5.4.8
future==0.15.2
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from distutils.core import setup

setup(name='pycaw',
version='20160929',
version='20181226',
description='Python Core Audio Windows Library',
author='Andre Miras',
url='https://github.com/AndreMiras/pycaw',
Expand Down
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
flake8==2.5.4
isort==4.3.4
9 changes: 8 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[tox]
envlist = pep8,py27,py35
envlist = pep8,py27,py36
skipsdist = True

[testenv]
deps = -r{toxinidir}/requirements.txt
Expand All @@ -8,3 +9,9 @@ commands = python -m unittest discover []

[testenv:pep8]
commands = flake8

[testenv:isort]
commands = isort --check-only --recursive --diff pycaw/ examples/

[flake8]
exclude = */venv/*

0 comments on commit c240c17

Please sign in to comment.