from typing import Optional, Self, Type, cast from PyQt6.QtCore import QObject, Qt, QUrl, pyqtSlot from PyQt6.QtMultimedia import ( QAudioDevice, QAudioOutput, QMediaDevices, QMediaPlayer, QSoundEffect, ) # from PyQt6.QtWidgets import QWidget class SoundOff(QObject): _instance = None def __new__(cls: Type[Self]) -> Self: if cls._instance: return cls._instance cls._instance = super(SoundOff, cls).__new__(cls) return cls._instance def __init__(self) -> None: super().__init__() # # Setup devices # self.virtualDevice = None dev = None for output in QMediaDevices.audioOutputs(): if output.id().data().decode("utf-8") == "virt-input": self.virtualDevice = output if output.isDefault(): self.localDevice = output self.alertEffect = QSoundEffect() self.alertEffect.setSource(QUrl("qrc:/beep.wav")) self.alertEffect.setAudioDevice(self.localDevice) self.alertEffect.setVolume(0.25) self.alertEffect.setLoopCount(1) self.localPlayer = QMediaPlayer() self.localPlayer.setObjectName("localPlayer") self.localOutput = QAudioOutput() self.localOutput.setDevice(self.localDevice) self.localPlayer.setAudioOutput(self.localOutput) if self.virtualDevice: self.virtualPlayer = QMediaPlayer() self.virtualPlayer.setObjectName("virtualPlayer") self.virtualOutput = QAudioOutput() self.virtualOutput.setDevice(self.virtualDevice) self.virtualPlayer.setAudioOutput(self.virtualOutput) # # Connections # self.localPlayer.errorOccurred.connect(self.mediaError) self.localPlayer.mediaStatusChanged.connect(self.mediaStatus) self.localPlayer.playbackStateChanged.connect(self.playbackState) if self.virtualDevice: self.virtualPlayer.errorOccurred.connect(self.mediaError) self.virtualPlayer.mediaStatusChanged.connect(self.mediaStatus) self.virtualPlayer.playbackStateChanged.connect(self.playbackState) @pyqtSlot() def alert(self) -> None: self.alertEffect.play() return @pyqtSlot(QMediaPlayer.Error, str) def mediaError(self, error: QMediaPlayer.Error, string: str) -> None: print(error) print(str) return @pyqtSlot(QMediaPlayer.MediaStatus) def mediaStatus(self, status: QMediaPlayer.MediaStatus) -> None: if status == QMediaPlayer.MediaStatus.LoadedMedia: player: Optional[QMediaPlayer] = cast(QMediaPlayer, self.sender()) assert player is not None player.play() return @pyqtSlot(QMediaPlayer.PlaybackState) def playbackState(self, state: QMediaPlayer.PlaybackState) -> None: return # # Communications slots # @pyqtSlot() def soundAlert(self) -> None: self.alertEffect.play() return @pyqtSlot(str) def playSound(self, url: str) -> None: src = QUrl(url) if not self.localPlayer.audioOutput(): self.localPlayer.setAudioOutput(self.localOutput) self.localPlayer.setSource(src) self.localPlayer.setPosition(0) if ( self.localPlayer.mediaStatus() == QMediaPlayer.MediaStatus.LoadedMedia ): self.localPlayer.play() if not self.virtualDevice: return self.virtualPlayer.setSource(src) self.virtualPlayer.setPosition(0) if not self.virtualPlayer.audioOutput(): self.virtualPlayer.setAudioOutput(self.virtualOutput) if ( self.virtualPlayer.mediaStatus() == QMediaPlayer.MediaStatus.LoadedMedia ): self.virtualPlayer.play() return