must
/
backend_winmm.py
123 строки · 4.5 Кб
1import os2import ctypes3import backend_base4import log5
6
7class WinMMWrapper(backend_base.BaseWrapper):8def __init__(self, mm_lib: ctypes.CDLL) -> None:9super().__init__()10self.lib = mm_lib11if not self.lib:12raise FileNotFoundError('Failed to load Windows MultiMedia library')13self.mciSendStringW = self.wrap('mciSendStringW', args=(14ctypes.c_wchar_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p15), res=ctypes.c_ulong)16self.mciGetErrorStringW = self.wrap('mciGetErrorStringW', args=(17ctypes.c_ulong, ctypes.c_void_p, ctypes.c_uint18), res=ctypes.c_int)19
20
21class WinMMMusic(backend_base.BaseMusic):22def __init__(self, bk: any, fp: str, alias: str) -> None:23super().__init__(fp)24self.bk = bk25self.al = alias26self.type = os.path.splitext(fp)[-1][1:]27try:28self.length = float(bk.send_warn(f'status {self.al} length', 'Failed to get music length')) / 100029except ValueError:30self.length = 0.031
32def play(self) -> None:33self.bk.send_warn(f'play {self.al}', 'Failed to play music')34
35def stop(self) -> None:36self.bk.send_warn(f'stop {self.al}', 'Failed to stop music')37
38def is_playing(self) -> bool:39return self.bk.send_warn(f'status {self.al} mode', 'Failed to get music state') in ('playing', 'paused')40
41def set_volume(self, volume: float = 1.0) -> None:42self.bk.send_warn(f'setaudio {self.al} volume to {round(volume * 1000)}', 'Failed to set music volume')43
44def set_speed(self, volume: float = 1.0) -> None:45self.bk.send_warn(f'set {self.al} speed {round(volume * 1000)}', 'Failed to set music speed')46
47def set_paused(self, paused: bool) -> None:48if paused == self.paused:49return50self.paused = paused51self.bk.send_warn(('pause ' if paused else 'resume ') + self.al, 'Failed to set music paused')52
53def set_pos(self, pos: float) -> None:54self.bk.send_warn(f'seek {self.al} to {round(pos * 1000)}', 'Failed to set music position')55self.bk.send_warn(f'play {self.al}', 'Failed to play music')56
57def get_pos(self) -> float:58res = self.bk.send_warn(f'status {self.al} position', 'Failed to get music position')59try:60return float(res) / 100061except ValueError:62return 0.063
64def rewind(self) -> None:65self.set_pos(0.0)66
67def destroy(self) -> None:68self.bk.send_warn(f'close {self.al}', 'Failed to close music')69self.bk = None70
71
72class WinMMBackend(backend_base.BaseBackend):73def __init__(self, app: any, mm_lib: ctypes.CDLL) -> None:74super().__init__()75self.title = 'WindowsMultimedia'76self.app = app77self.buffer_size = 102478self.mm = WinMMWrapper(mm_lib)79
80def open_music(self, fp: str) -> WinMMMusic:81alias = os.path.basename(fp).replace(' ', '')82self.send_err(f'open "{fp}" alias {alias}', 'Failed to open music')83self.send_warn(f'set {alias} time format milliseconds', 'Failed to set time format for music')84return WinMMMusic(self, fp, alias)85
86def destroy(self) -> None:87self.mm = None88self.app = None89
90def get_audio_devices_names(self) -> list:91return ['Default Device']92
93def get_audio_drivers(self) -> list:94return ['winmm']95
96def get_current_audio_driver(self) -> str:97return 'winmm'98
99def get_current_audio_device_name(self) -> str:100return 'Default Device'101
102def send(self, command: str) -> tuple:103buffer = ctypes.create_unicode_buffer(self.buffer_size + 1)104code = self.mm.mciSendStringW(command, buffer, self.buffer_size, None)105if code:106new_buf = ctypes.create_unicode_buffer(self.buffer_size + 1)107if self.mm.mciGetErrorStringW(code, buffer, self.buffer_size):108return new_buf.value, True109return 'Unknown Error', True110return buffer.value, False111
112def send_err(self, command: str, error_text: str) -> str:113resp, is_err = self.send(command)114if is_err:115raise RuntimeError(error_text + ' (' + resp + ')')116return resp117
118def send_warn(self, command: str, error_text: str) -> str:119resp, is_err = self.send(command)120if is_err:121log.warn(error_text + ' (' + resp + ')')122return ''123return resp124