text: import os
  import subprocess
  import sys
  import threading
  import wave
  from typing import Callable, Optional
  import simpleaudio as sa
  
  
  class WavFile:
      """
      Class contains parameters of sound file.
      """
  
      def __init__(self, path: str):
          self.wave_object = sa.WaveObject.from_wave_file(path)
          self.file_name = path
          with wave.open(path, "rb") as file:
              self.width = file.getsampwidth()
              self.channels = file.getnchannels()
              self.rate = file.getframerate()
              self.frames = file.getnframes()
              self.data = file.readframes(self.frames)
              self.duration = float(self.frames) / self.rate
  
  
  def mute(func: Callable):
      def decorated(self, *args, **kwargs):
          if self._mute:
              return
          return func(self, *args, **kwargs)
      return decorated
  
  
  class WavPlayer:
      """
      Class loads sounds from files and plays sounds.
      """
  
      def __init__(self, wait: bool = True, device: Optional[str] = None, channels: Optional[int] = None):
          """
          :param wait: play sound in sync mode;
          :param device: device to play sound on (valid for Linux).
          """
  
          self.sounds = dict()
          self._channels = channels
          self._device = device
          self._mute = False
          self._threads = []
          self._wait = wait
  
          if sys.platform.startswith("linux"):
              self.play = self.__play_linux
          elif sys.platform.startswith("win"):
              import winsound
              self.winsound = winsound
              self.play = self.__play_win
          else:
              self.play = self.__play_sa
  
      def add_sound(self, file_name: str, sound_name: str):
          """
          Method creates WavFile-object with sound and adds it to dictionary.
          :param file_name: name of file with sound;
          :param sound_name: name of sound.
          """
  
          self.sounds[sound_name] = WavFile(file_name)
  
      def check_sound_available(self) -> bool:
          """
          Method checks if it is possible to play sound files in system.
          :return: True if it is possible.
          """
  
          path_to_dummy_wav = os.path.join(os.path.dirname(__file__), "void.wav")
          self.add_sound(path_to_dummy_wav, "epsound_test_sound_for_driver_checking")
          try:
              self.play("epsound_test_sound_for_driver_checking")
              return True
          except RuntimeError:
              return False
  
      def is_mute(self) -> bool:
          return self._mute
  
      def remove_channels(self):
          """
          Method removes channels.
          """
  
          self._channels = None
  
      def remove_device(self):
          """
          Method removes audio device.
          """
  
          self._device = None
  
      def set_channels(self, channels: int):
          """
          Method sets number of used channels.
          :param channels: number of used channels.
          """
  
          if channels:
              self._channels = channels
          else:
              self._channels = None
  
      def set_device(self, device: str):
          """
          Method sets audio device to play sound. Method is relevant for Linux only.
          :param device: audio device.
          """
  
          if device:
              self._device = device
          else:
              self._device = None
  
      def set_mute(self, state: bool = True):
          self._mute = state
  
      def stop(self):
          """
          Function stop thread with sound
          :return:
          """
          for th in self._threads:
              th.join()
  
      @mute
      def __play_linux(self, sound_name: str):
          """
          Method plays sound on Linux.
          :param sound_name: name of sound to be played.
          """
  
          fh = open(os.devnull, "wb")
          args = ["aplay",]
          if self._device:
              args.extend(["--device", self._device])
          if self._channels:
              args.extend(["--channels", str(self._channels)])
          args.append(self.sounds[sound_name].file_name)
          proc = subprocess.Popen(args, stdout=fh, stderr=fh)
          if self._wait:
              proc.wait()
          fh.close()
  
      @mute
      def __play_sa(self, sound_name: str):
          """
          Method plays sound in another OS.
          :param sound_name: name of sound to be played.
          """
  
          def _play():
              self.sounds[sound_name].wave_object.play()
  
          thread = threading.Thread(target=_play, args=())
          self._threads.append(thread)
          thread.start()
  
      @mute
      def __play_win(self, sound_name: str):
          """
          Method plays sound on Windows.
          :param sound_name: name of sound to be played.
          """
  
          flags = self.winsound.SND_NOSTOP
          if not self._wait:
              flags |= self.winsound.SND_ASYNC
          self.winsound.PlaySound(self.sounds[sound_name].file_name, flags)
  