Skip to content

Commit

Permalink
Merge branch 'release/v20220416'
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreMiras committed Apr 17, 2022
2 parents a93daff + ee1e7de commit 2a5d923
Show file tree
Hide file tree
Showing 34 changed files with 2,312 additions and 793 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/pypi-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: PyPI release

on: [push]

jobs:
pypi:
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- name: Install dependencies
run: python -m pip install --upgrade setuptools wheel twine
- name: Build
run: |
python setup.py sdist bdist_wheel
twine check dist/*
- name: Publish package
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
uses: pypa/[email protected]
with:
verbose: true
user: __token__
password: ${{ secrets.pypi_password }}
34 changes: 34 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Tests

on: [push]

jobs:
linter:
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- run: pip install tox
- run: tox -e pep8
- run: tox -e isort
test:
runs-on: windows-2019
strategy:
matrix:
python: ['3.7', '3.8', '3.9', '3.10']

steps:
- uses: actions/checkout@v2
# Virtual network sound card for Microsoft Windows
- name: Install Scream
shell: powershell
run: |
Invoke-WebRequest https://github.com/duncanthrax/scream/releases/download/3.8/Scream3.8.zip -OutFile Scream3.8.zip
Expand-Archive -Path Scream3.8.zip -DestinationPath Scream
Import-Certificate -FilePath Scream\Install\driver\x64\Scream.cat -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
Scream\Install\helpers\devcon-x64.exe install Scream\Install\driver\x64\Scream.inf *Scream
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- run: pip install tox
- run: tox -e py
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Custom
venv/
.vscode/
*.swp

# Byte-compiled / optimized / DLL files
Expand Down
19 changes: 0 additions & 19 deletions .travis.yml

This file was deleted.

17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Change Log

## [20220416]
- Migrate to GitHub Actions
- CI tests via virtual sound card
- Drop Python 2.7 and 3.6
- Add support for 3.7 up to 3.10
- Fix missing api package, refs #63 (@Jan-Zeiseweis)
- Automatic PyPI release on tagging, refs #44

## [20210516]
- Fixed GetAllDevices() COMError, refs #15, #28 and #30 (@micolous and @reversefold)
- New IAudioSessionEvents callbacks support, refs #27, #36 (@TurboAnonym)
- IAudioSessionControl GetState fix, refs #32, #37 (@TurboAnonym)
- Adding AudioUtilities.GetMicrophone(), refs #39 (@alebzk)
- Reorganize / Split the pycaw.pycaw file in modules and subpackages, refs #38 (@TurboAnonym)
- OnSessionCreated support + Wrapper for callbacks, refs #40 (@TurboAnonym)
- "Magic" module, easy session control, refs #42 (@TurboAnonym)

## [20190904]
- Fixed enum34 dependency, refs #17 (@mmxfguerin)

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# pycaw

[![Build Status](https://travis-ci.org/AndreMiras/pycaw.svg?branch=develop)](https://travis-ci.org/AndreMiras/pycaw)
[![Tests](https://github.com/AndreMiras/pycaw/actions/workflows/tests.yml/badge.svg)](https://github.com/AndreMiras/pycaw/actions/workflows/tests.yml)
[![PyPI release](https://github.com/AndreMiras/pycaw/actions/workflows/pypi-release.yml/badge.svg)](https://github.com/AndreMiras/pycaw/actions/workflows/pypi-release.yml)
[![PyPI version](https://badge.fury.io/py/pycaw.svg)](https://badge.fury.io/py/pycaw)

Python Core Audio Windows Library, working for both Python2 and Python3.
Expand Down
48 changes: 48 additions & 0 deletions docs/Release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# How to release

This is documenting the release process.


## Git flow & CHANGELOG.md

Make sure the CHANGELOG.md is up to date and follows the http://keepachangelog.com guidelines.
Start the release with git flow:
```batch
git flow release start vYYYYMMDD
```
Now update the [CHANGELOG.md](/CHANGELOG.md) `[Unreleased]` section to match the new release version.
Also update the `version` string in the [setup.py](/setup.py) file. Then commit and finish release.
```batch
git commit -a -m ":bookmark: vYYYYMMDD"
git flow release finish
```
Push everything, make sure tags are also pushed:
```batch
git push
git push origin main:main
git push --tags
```

## Publish to PyPI
This process is handled automatically by [GitHub Actions](https://github.com/AndreMiras/pycaw/actions/workflows/pypi-release.yml).
If needed below are the instructions to perform it manually.
Build it:
```batch
python setup.py sdist bdist_wheel
```
Check archive content:
```batch
tar -tvf dist\pycaw-*.tar.gz
```
Upload:
```batch
twine upload dist\pycaw-*.tar.gz
```

## GitHub

Got to GitHub [Release/Tags](https://github.com/AndreMiras/pycaw/tags), click "Add release notes" for the tag just created.
Add the tag name in the "Release title" field and the relevant CHANGELOG.md section in the "Describe this release" textarea field.

## Post release
Update the [setup.py](/setup.py) `version string` with `YYYYMMDD.dev0`.
4 changes: 1 addition & 3 deletions examples/audio_controller_class_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
Per session GetMute() SetMute() GetMasterVolume() SetMasterVolume() using
SimpleAudioVolume.
"""
from __future__ import print_function

from pycaw.pycaw import AudioUtilities


class AudioController(object):
class AudioController:
def __init__(self, process_name):
self.process_name = process_name
self.volume = self.process_volume()
Expand Down
2 changes: 0 additions & 2 deletions examples/audio_endpoint_volume_example.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"""
Get and set access to master volume example.
"""
from __future__ import print_function

from ctypes import POINTER, cast

from comtypes import CLSCTX_ALL
Expand Down
101 changes: 101 additions & 0 deletions examples/magic_app_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""
Note
----
'import pycaw.magic' must be generally at the topmost.
To be more specific:
It needs to be imported before any other pycaw or comtypes import.
Reserved Atrributes
-------------------
Note that certain methods and attributes are reserved for the magic module.
Please look into the source code of MagicApp for more information.
But to avoid conflicts now and in the future, i recommend using
a prefix for each of your custom methods and attributes.
Features
--------
Instantiate a new MagicApp with one or more app executables:
magic = MagicApp({"msedge.exe", "another.exe"})
--------
you could also inherit from MagicApp and create customized callbacks:
class MyCustomApp(MagicApp):
def __init__(self, app_execs):
super().__init__(app_execs,
volume_callback=self.custom_volume_callback,
mute_callback=self...,
state_callback=self...,
session_callback=self...)
def custom_volume_callback(self, volume):
print(volume)
print(self.mute)
self.mute = True
print(self.mute)
mega_magic = MyCustomApp({"msedge.exe"})
"""

import time
from contextlib import suppress

from pycaw.magic import MagicApp


def handle_all(*args):
print("callback")
print(args)


magic = MagicApp({"msedge.exe"},
volume_callback=handle_all,
mute_callback=handle_all,
state_callback=handle_all,
session_callback=handle_all)


def main():
with suppress(KeyboardInterrupt):
for _ in range(5):
"""
open and close your MagicApp app_exec (msedge.exe)
and see how it will change the volume as long as
the app is opened. When you close app_exec it wont change
the volume and None is printed.
if you change for example the volume in the Windows sound mixer
handle_all() is fired.
"""

if magic.state is None:
print(f"No session active for: {magic}")
time.sleep(2)
continue

print("Volume:")
magic.volume = 0.1
print(magic.volume)
time.sleep(1)
magic.volume = 0.9
print(magic.volume)
time.sleep(1)

print(f"{str(magic.state)} {magic.app_execs}")

print("Mute:")
magic.mute = True
print(magic.mute)
time.sleep(1)
magic.mute = False
print(magic.mute)

print("\nTschüss")


if __name__ == '__main__':
main()
93 changes: 93 additions & 0 deletions examples/session_callback_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Enter the right 'app_name' and play something to initiate a WASAPI session.
Then launch this file ;)
Requirements: Python >= 3.6 - f strings ;)
following "IAudioSessionEvents" callbacks are supported:
:: Gets called on volume and mute change:
IAudioSessionEvents.OnSimpleVolumeChanged()
-> on_simple_volume_changed()
:: Gets called on session state change (active/inactive/expired):
IAudioSessionEvents.OnStateChanged()
-> on_state_changed()
:: Gets called on for example Speaker unplug
IAudioSessionEvents.OnSessionDisconnected()
-> on_session_disconnected()
https://docs.microsoft.com/en-us/windows/win32/api/audiopolicy/nn-audiopolicy-iaudiosessionevents
"""
import time

from comtypes import COMError

from pycaw.callbacks import AudioSessionEvents
from pycaw.utils import AudioUtilities

app_name = "msedge.exe"


class MyCustomCallback(AudioSessionEvents):

def on_simple_volume_changed(self, new_volume, new_mute, event_context):
print(':: OnSimpleVolumeChanged callback\n'
f"new_volume: {new_volume}; "
f"new_mute: {new_mute}; "
f"event_context: {event_context.contents}")

def on_state_changed(self, new_state, new_state_id):
print(':: OnStateChanged callback\n'
f"new_state: {new_state}; id: {new_state_id}")

def on_session_disconnected(self, disconnect_reason, disconnect_reason_id):
print(':: OnSessionDisconnected callback\n'
f"disconnect_reason: {disconnect_reason}; "
f"id: {disconnect_reason_id}")


def add_callback(app_name):
try:
sessions = AudioUtilities.GetAllSessions()
except COMError:
exit("No speaker set up")

# grap the right session
app_found = False
for session in sessions:
if session.Process and session.Process.name() == app_name:

app_found = True
callback = MyCustomCallback()
# Adding the callback
session.register_notification(callback)

if not app_found:
exit("Enter the right 'app_name', start it and play something")

print("Ready to go!")
print("Change the volume / mute state "
"/ close the app or unplug your speaker")
print("and watch the callbacks ;)\n")

try:
# wait 300 seconds for callbacks
time.sleep(300)
except KeyboardInterrupt:
pass
finally:

# unregister callback(s)
# unregister_notification()
# (only if it was also registered.)
# pycaw.utils -> unregister_notification()
for session in sessions:
session.unregister_notification()

print("\nTschüss")


if __name__ == "__main__":
add_callback(app_name)
2 changes: 0 additions & 2 deletions examples/simple_audio_volume_example.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"""
Per session GetMute() SetMute() using ISimpleAudioVolume.
"""
from __future__ import print_function

from pycaw.pycaw import AudioUtilities, ISimpleAudioVolume


Expand Down
Loading

0 comments on commit 2a5d923

Please sign in to comment.