diff --git a/voicemeeterlib/bus.py b/voicemeeterlib/bus.py index 44a7516..dd50653 100644 --- a/voicemeeterlib/bus.py +++ b/voicemeeterlib/bus.py @@ -210,7 +210,7 @@ class BusLevel(IRemote): def fget(x): return round(20 * log(x, 10), 1) if x > 0 else -200.0 - if self._remote.running and self._remote.event.ldirty: + if not self._remote.stopped() and self._remote.event.ldirty: vals = self._remote.cache["bus_level"][self.range[0] : self.range[-1]] else: vals = [self._remote.get_level(mode, i) for i in range(*self.range)] @@ -232,7 +232,7 @@ class BusLevel(IRemote): Expected to be used in a callback only. """ - if self._remote.running: + if not self._remote.stopped(): return any(self._remote._bus_comp[self.range[0] : self.range[-1]]) is_updated = isdirty diff --git a/voicemeeterlib/remote.py b/voicemeeterlib/remote.py index 2c8495e..0bbd47a 100644 --- a/voicemeeterlib/remote.py +++ b/voicemeeterlib/remote.py @@ -1,5 +1,6 @@ import ctypes as ct import logging +import threading import time from abc import abstractmethod from queue import Queue @@ -28,11 +29,12 @@ class Remote(CBindings): self.cache = {} self.midi = Midi() self.subject = self.observer = Subject() - self.running = False self.event = Event( {k: kwargs.pop(k) for k in ("pdirty", "mdirty", "midi", "ldirty")} ) self.gui = VmGui() + self.stop_event = threading.Event() + self.stop_event.clear() self.logger = logger.getChild(self.__class__.__name__) for attr, val in kwargs.items(): @@ -52,16 +54,18 @@ class Remote(CBindings): def init_thread(self): """Starts updates thread.""" - self.running = True self.event.info() self.logger.debug("initiating events thread") queue = Queue() self.updater = Updater(self, queue) self.updater.start() - self.producer = Producer(self, queue) + self.producer = Producer(self, queue, self.stop_event) self.producer.start() + def stopped(self): + return self.stop_event.is_set() + def login(self) -> None: """Login to the API, initialize dirty parameters""" self.gui.launched = self.call(self.bind_login, ok=(0, 1)) == 0 @@ -331,9 +335,10 @@ class Remote(CBindings): self.logger.info(f"Profile '{name}' applied!") def end_thread(self): - if self.running: + if not self.stopped(): self.logger.debug("events thread shutdown started") - self.running = False + self.stop_event.set() + self.producer.join() # wait for producer thread to complete cycle def logout(self) -> None: """Logout of the API""" diff --git a/voicemeeterlib/strip.py b/voicemeeterlib/strip.py index edcbe62..9788274 100644 --- a/voicemeeterlib/strip.py +++ b/voicemeeterlib/strip.py @@ -415,7 +415,7 @@ class StripLevel(IRemote): def fget(x): return round(20 * log(x, 10), 1) if x > 0 else -200.0 - if self._remote.running and self._remote.event.ldirty: + if not self._remote.stopped() and self._remote.event.ldirty: vals = self._remote.cache["strip_level"][self.range[0] : self.range[-1]] else: vals = [self._remote.get_level(mode, i) for i in range(*self.range)] @@ -448,7 +448,7 @@ class StripLevel(IRemote): Expected to be used in a callback only. """ - if self._remote.running: + if not self._remote.stopped(): return any(self._remote._strip_comp[self.range[0] : self.range[-1]]) is_updated = isdirty diff --git a/voicemeeterlib/updater.py b/voicemeeterlib/updater.py index b5f10ff..331634c 100644 --- a/voicemeeterlib/updater.py +++ b/voicemeeterlib/updater.py @@ -10,14 +10,18 @@ logger = logging.getLogger(__name__) class Producer(threading.Thread): """Continously send job queue to the Updater thread at a rate of self._remote.ratelimit.""" - def __init__(self, remote, queue): - super().__init__(name="producer", daemon=True) + def __init__(self, remote, queue, stop_event): + super().__init__(name="producer", daemon=False) self._remote = remote self.queue = queue + self.stop_event = stop_event self.logger = logger.getChild(self.__class__.__name__) + def stopped(self): + return self.stop_event.is_set() + def run(self): - while self._remote.running: + while not self.stopped(): if self._remote.event.pdirty: self.queue.put("pdirty") if self._remote.event.mdirty: