from typing import Optional, Self, Type, cast from PyQt6.QtCore import ( QBuffer, QByteArray, QDir, QObject, QStandardPaths, QUrl, pyqtSlot, ) from PyQt6.QtMultimedia import ( QAudioOutput, QMediaDevices, QMediaPlayer, QSoundEffect, ) from PyQt6.QtNetwork import ( QNetworkAccessManager, QNetworkDiskCache, QNetworkRequest, ) # 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 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.setVolume(1.0) 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) cacheDir = QDir( QStandardPaths.writableLocation( QStandardPaths.StandardLocation.GenericCacheLocation ) ) cacheDir.mkdir("Troglodite") cacheDir = QDir(cacheDir.path() + QDir.separator() + "Troglodite") netCache = QNetworkDiskCache() netCache.setCacheDirectory(cacheDir.path()) self.nam = QNetworkAccessManager() self.nam.setCache(netCache) return @pyqtSlot(QMediaPlayer.Error, str) def mediaError(self, error: QMediaPlayer.Error, msg: str) -> None: print(error) print(msg) return @pyqtSlot(QMediaPlayer.MediaStatus) def mediaStatus(self, status: QMediaPlayer.MediaStatus) -> None: print(f"mediaStatus: {status}") 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: print(f"playbackState: {state}") return # # Communications slots # @pyqtSlot() def soundAlert(self) -> None: self.alertEffect.play() return @pyqtSlot(str) def playSound(self, url: str | QUrl) -> None: if isinstance(url, str): url = QUrl(url) if not self.localPlayer.audioOutput(): self.localPlayer.setAudioOutput(self.localOutput) request = QNetworkRequest(url) self.reply = self.nam.get(request) assert self.reply is not None self.reply.finished.connect(self.finished) return @pyqtSlot() def alert(self) -> None: self.alertEffect.play() return # # Network slots # _buffer = QBuffer() _storage = QByteArray() @pyqtSlot() def finished(self) -> None: assert self.reply is not None self._storage = self.reply.readAll() if self.localPlayer.sourceDevice() != self._buffer: self._buffer.setBuffer(self._storage) self.localPlayer.setSourceDevice(self._buffer) self.localPlayer.setPosition(0) if ( self.localPlayer.mediaStatus() == QMediaPlayer.MediaStatus.LoadedMedia ): self.localPlayer.play() print("play") if not self.virtualDevice: return if self.virtualPlayer.sourceDevice() != self._buffer: self.virtualPlayer.setSourceDevice(self._buffer) 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