From 364807771a542f251ae2286dc591db759cae5e6e Mon Sep 17 00:00:00 2001 From: Henning Bock Date: Wed, 31 Dec 2025 15:33:04 +0100 Subject: [PATCH] Initial Release --- generator.py | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100755 generator.py diff --git a/generator.py b/generator.py new file mode 100755 index 0000000..0909965 --- /dev/null +++ b/generator.py @@ -0,0 +1,126 @@ +#!/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())