#!/usr/bin/env python import sys import numpy as np import pyaudio import threading from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QPushButton, QSlider, QLabel, QSystemTrayIcon, QMenu) from PyQt6.QtCore import Qt from PyQt6.QtGui import QIcon, QAction, QColor, QPixmap class BrownNoiseGenerator(QWidget): def __init__(self): super().__init__() # Audio-Setup self.fs = 44100 self.chunk_size = 1024 self.volume = 0.5 self.is_playing = False self.p = pyaudio.PyAudio() self.stream = None self.last_val = 0.0 self.init_ui() self.setup_tray() def init_ui(self): self.setWindowTitle("Brown Noise") self.setFixedWidth(250) layout = QVBoxLayout() self.btn_toggle = QPushButton("Start", self) self.btn_toggle.clicked.connect(self.toggle_noise) layout.addWidget(self.btn_toggle) layout.addWidget(QLabel("Lautstärke:")) self.slider = QSlider(Qt.Orientation.Horizontal) self.slider.setRange(0, 100) self.slider.setValue(50) self.slider.valueChanged.connect(self.update_volume) layout.addWidget(self.slider) self.setLayout(layout) def setup_tray(self): # Erstellt ein einfaches Icon (farbiges Quadrat), falls du keine .png Datei hast pixmap = QPixmap(64, 64) pixmap.fill(QColor("brown")) icon = QIcon(pixmap) self.tray_icon = QSystemTrayIcon(icon, self) self.tray_icon.setToolTip("Brown Noise Generator") # Tray Menü (Rechtsklick) tray_menu = QMenu() show_action = QAction("Anzeigen", self) quit_action = QAction("Beenden", self) show_action.triggered.connect(self.show) quit_action.triggered.connect(self.quit_app) tray_menu.addAction(show_action) tray_menu.addSeparator() tray_menu.addAction(quit_action) self.tray_icon.setContextMenu(tray_menu) self.tray_icon.show() # Linksklick auf Icon zeigt/versteckt Fenster self.tray_icon.activated.connect(self.tray_icon_activated) def tray_icon_activated(self, reason): if reason == QSystemTrayIcon.ActivationReason.Trigger: if self.isVisible(): self.hide() else: self.show() self.raise_() def update_volume(self, value): self.volume = value / 100.0 def generate_brown_noise_chunk(self): white_noise = np.random.uniform(-1, 1, self.chunk_size) brown_noise = np.zeros_like(white_noise) for i in range(len(white_noise)): self.last_val = 0.99 * self.last_val + 0.02 * white_noise[i] brown_noise[i] = self.last_val return (brown_noise * self.volume).astype(np.float32).tobytes() def audio_loop(self): self.stream = self.p.open(format=pyaudio.paFloat32, channels=1, rate=self.fs, output=True) while self.is_playing: self.stream.write(self.generate_brown_noise_chunk()) self.stream.stop_stream() self.stream.close() def toggle_noise(self): if not self.is_playing: self.is_playing = True self.btn_toggle.setText("Stop") threading.Thread(target=self.audio_loop, daemon=True).start() else: self.is_playing = False self.btn_toggle.setText("Start") def closeEvent(self, event): """Überschreibt das Schließen: Fenster wird nur versteckt.""" if self.tray_icon.isVisible(): self.hide() event.ignore() # Verhindert das Schließen der App def quit_app(self): """Beendet die App wirklich.""" self.is_playing = False self.p.terminate() QApplication.quit() if __name__ == "__main__": app = QApplication(sys.argv) # Verhindert, dass die App schließt, wenn das Fenster versteckt wird app.setQuitOnLastWindowClosed(False) ex = BrownNoiseGenerator() ex.show() sys.exit(app.exec())