AndreMiras / pycaw

Python Core Audio Windows Library
MIT License
385 stars 67 forks source link

Conflict keyboard package #34

Open ghost opened 3 years ago

ghost commented 3 years ago

Hello! I try to use pycaw with keyboard package. https://github.com/boppreh/keyboard

If you run this code and press "4" then error occurs.

from pycaw.pycaw import AudioUtilities
import keyboard
import time

keyboard.add_hotkey('4', AudioUtilities.GetAllSessions)

time.sleep(3600*24*30)
Exception in thread Thread-2:
Traceback (most recent call last):
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 932, in _bootstrap_inner
    self.run()
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\site-packages\keyboard\_generic.py", line 58, in process
    if self.pre_process_event(event):
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\site-packages\keyboard\__init__.py", line 218, in pre_process_event
    callback(event)
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\site-packages\keyboard\__init__.py", line 649, in <lambda>
    handler = lambda e: (event_type == KEY_DOWN and e.event_type == KEY_UP and e.scan_code in _logically_pressed_keys) or (event_type == e.event_type and callback())
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\site-packages\pycaw\pycaw.py", line 669, in GetAllSessions
    mgr = AudioUtilities.GetAudioSessionManager()
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\site-packages\pycaw\pycaw.py", line 657, in GetAudioSessionManager
    speakers = AudioUtilities.GetSpeakers()
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\site-packages\pycaw\pycaw.py", line 647, in GetSpeakers
    deviceEnumerator = comtypes.CoCreateInstance(
  File "C:\Users\user1\AppData\Local\Programs\Python\Python38-32\lib\site-packages\comtypes\__init__.py", line 1219, in CoCreateInstance
    _ole32.CoCreateInstance(byref(clsid), punkouter, clsctx, byref(iid), byref(p))
  File "_ctypes/callproc.c", line 948, in GetResult
OSError: [WinError -2147221008] CoInitialize has not been called

pip freeze comtypes==1.1.9 enum34==1.1.10 future==0.18.2 keyboard==0.13.5 psutil==5.8.0 pycaw==20181226

TurboAnonym commented 3 years ago

Pycaw uses the comtypes library for using COM. For the communication with windows its necessary to comtypes.CoInitialize() (at the start) and comtypes.CoUninitialize() (in the end) in every thread. (If you want more information look at COM Apartment threaded.) If you use pycaw in the main thread of your script CoInitialize() will be called on import of the module and CoUninitialize() on close. But when you are using multiple threads in your python script (indirectly for example by using the library keyboard) then you must tell the thread to comtypes.CoInitialize() and to comtypes.CoUninitialize()

Following is stated in the comtypes.__init__.py

# COM is initialized automatically for the thread that imports this
# module for the first time. [...]
#
# A shutdown function is registered with atexit, so that
# CoUninitialize is called when Python is shut down.

# We need to have CoUninitialize for multithreaded model where we have
# to initialize and uninitialize COM for every new thread (except main)
# in which we are using COM

so implementing that would look the following (and it works too ;) )

from pycaw.pycaw import AudioUtilities
from comtypes import CoInitialize, CoUninitialize
import keyboard
import time

def audio_stuff():
    CoInitialize()
    sessions = AudioUtilities.GetAllSessions()
    for session in sessions:
        if session.Process:
            print(str(session))
    CoUninitialize()

keyboard.add_hotkey('4', audio_stuff)

time.sleep(3600*24*30)