mirror of
				https://github.com/onyx-and-iris/voicemeeter-api-python.git
				synced 2025-10-31 13:51:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			198 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import argparse
 | |
| import logging
 | |
| import time
 | |
| from abc import ABC, abstractmethod
 | |
| from enum import IntEnum
 | |
| 
 | |
| from pyparsing import (
 | |
|     Combine,
 | |
|     Group,
 | |
|     OneOrMore,
 | |
|     Optional,
 | |
|     Suppress,
 | |
|     Word,
 | |
|     alphanums,
 | |
|     nums,
 | |
| )
 | |
| 
 | |
| import voicemeeterlib
 | |
| 
 | |
| logging.basicConfig(level=logging.DEBUG)
 | |
| logger = logging.getLogger(__name__)
 | |
| 
 | |
| 
 | |
| argparser = argparse.ArgumentParser(description="creates a basic dsl")
 | |
| argparser.add_argument("-i", action="store_true")
 | |
| args = argparser.parse_args()
 | |
| 
 | |
| 
 | |
| ParamKinds = IntEnum(
 | |
|     "ParamKinds",
 | |
|     "bool float string",
 | |
| )
 | |
| 
 | |
| 
 | |
| class Strategy(ABC):
 | |
|     def __init__(self, target, param, val):
 | |
|         self.target = target
 | |
|         self.param = param
 | |
|         self.val = val
 | |
| 
 | |
|     @abstractmethod
 | |
|     def run(self):
 | |
|         pass
 | |
| 
 | |
| 
 | |
| class BoolStrategy(Strategy):
 | |
|     def run(self):
 | |
|         setattr(self.target, self.param, self.strtobool(self.val))
 | |
| 
 | |
|     def strtobool(self, val):
 | |
|         """Convert a string representation of truth to it's numeric form."""
 | |
| 
 | |
|         val = val.lower()
 | |
|         if val in ("y", "yes", "t", "true", "on", "1"):
 | |
|             return 1
 | |
|         elif val in ("n", "no", "f", "false", "off", "0"):
 | |
|             return 0
 | |
|         else:
 | |
|             raise ValueError("invalid truth value %r" % (val,))
 | |
| 
 | |
| 
 | |
| class FloatStrategy(Strategy):
 | |
|     def run(self):
 | |
|         setattr(self.target, self.param, float(self.val))
 | |
| 
 | |
| 
 | |
| class StringStrategy(Strategy):
 | |
|     def run(self):
 | |
|         setattr(self.target, self.param, " ".join(self.val))
 | |
| 
 | |
| 
 | |
| class Context:
 | |
|     def __init__(self, strategy: Strategy) -> None:
 | |
|         self._strategy = strategy
 | |
| 
 | |
|     @property
 | |
|     def strategy(self) -> Strategy:
 | |
|         return self._strategy
 | |
| 
 | |
|     @strategy.setter
 | |
|     def strategy(self, strategy: Strategy) -> None:
 | |
|         self._strategy = strategy
 | |
| 
 | |
|     def run(self):
 | |
|         self.strategy.run()
 | |
| 
 | |
| 
 | |
| class Parser:
 | |
|     IS_STRING = ("label",)
 | |
| 
 | |
|     def __init__(self, vm):
 | |
|         self.logger = logger.getChild(self.__class__.__name__)
 | |
|         self.vm = vm
 | |
|         self.kls = Group(OneOrMore(Word(alphanums)))
 | |
|         self.token = Suppress("->")
 | |
|         self.param = Group(OneOrMore(Word(alphanums)))
 | |
|         self.value = Combine(
 | |
|             Optional("-") + Word(nums) + Optional(".") + Optional(Word(nums))
 | |
|         ) | Group(OneOrMore(Word(alphanums)))
 | |
|         self.event = (
 | |
|             self.kls
 | |
|             + self.token
 | |
|             + self.param
 | |
|             + Optional(self.token)
 | |
|             + Optional(self.value)
 | |
|         )
 | |
| 
 | |
|     def converter(self, cmds):
 | |
|         """determines the kind of parameter from the parsed string"""
 | |
| 
 | |
|         res = list()
 | |
|         for cmd in cmds:
 | |
|             self.logger.debug(f"running command: {cmd}")
 | |
|             match cmd_parsed := self.event.parseString(cmd):
 | |
|                 case [[kls, index], [param]]:
 | |
|                     target = getattr(self.vm, kls)[int(index)]
 | |
|                     res.append(getattr(target, param))
 | |
|                 case [[kls, index], [param], val] if param in self.IS_STRING:
 | |
|                     target = getattr(self.vm, kls)[int(index)]
 | |
|                     context = self._get_context(ParamKinds.string, target, param, val)
 | |
|                     context.run()
 | |
|                 case [[kls, index], [param], [val] | val]:
 | |
|                     target = getattr(self.vm, kls)[int(index)]
 | |
|                     try:
 | |
|                         context = self._get_context(ParamKinds.bool, target, param, val)
 | |
|                         context.run()
 | |
|                     except ValueError as e:
 | |
|                         self.logger.error(f"{e}... switching to float strategy")
 | |
|                         context.strategy = FloatStrategy(target, param, val)
 | |
|                         context.run()
 | |
|                 case [
 | |
|                     [kls, index],
 | |
|                     [secondary, param],
 | |
|                     [val] | val,
 | |
|                 ]:
 | |
|                     primary = getattr(self.vm, kls)[int(index)]
 | |
|                     target = getattr(primary, secondary)
 | |
|                     try:
 | |
|                         context = self._get_context(ParamKinds.bool, target, param, val)
 | |
|                         context.run()
 | |
|                     except ValueError as e:
 | |
|                         self.logger.error(f"{e}... switching to float strategy")
 | |
|                         context.strategy = FloatStrategy(target, param, val)
 | |
|                         context.run()
 | |
|                 case _:
 | |
|                     self.logger.error(
 | |
|                         f"unable to determine the kind of parameter from {cmd_parsed}"
 | |
|                     )
 | |
|             time.sleep(0.05)
 | |
|         return res
 | |
| 
 | |
|     def _get_context(self, kind, *args):
 | |
|         """
 | |
|         determines a strategy for a kind of parameter and passes it to the context.
 | |
|         """
 | |
| 
 | |
|         match kind:
 | |
|             case ParamKinds.bool:
 | |
|                 context = Context(BoolStrategy(*args))
 | |
|             case ParamKinds.float:
 | |
|                 context = Context(FloatStrategy(*args))
 | |
|             case ParamKinds.string:
 | |
|                 context = Context(StringStrategy(*args))
 | |
|         return context
 | |
| 
 | |
| 
 | |
| def interactive_mode(parser):
 | |
|     while cmd := input("Please enter command (Press <Enter> to exit)\n"):
 | |
|         if res := parser.parse((cmd,)):
 | |
|             print(res)
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     # fmt: off
 | |
|     cmds = (
 | |
|         "strip 0 -> mute -> true", "strip 0 -> mute", "bus 0 -> mute -> true",
 | |
|         "strip 0 -> mute -> false", "bus 0 -> mute -> true", "strip 3 -> solo -> true",
 | |
|         "strip 3 -> solo -> false", "strip 1 -> A1 -> true", "strip 1 -> A1",
 | |
|         "strip 1 -> A1 -> false", "strip 1 -> A1", "strip 3 -> eq on -> true",
 | |
|         "bus 3 -> eq on -> false", "strip 4 -> gain -> 1.2", "strip 0 -> gain -> -8.2",
 | |
|         "strip 0 -> gain", "strip 1 -> label -> rode podmic", "strip 2 -> limit -> -28",
 | |
|         "strip 2 -> limit", "strip 3 -> comp knob -> 3.8"
 | |
|     )
 | |
|     # fmt: on
 | |
| 
 | |
|     with voicemeeterlib.api("potato") as vm:
 | |
|         parser = Parser(vm)
 | |
|         if args.i:
 | |
|             interactive_mode(parser)
 | |
|             return
 | |
| 
 | |
|         if res := parser.converter(cmds):
 | |
|             print(res)
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     main()
 |