Files
Brown-Noise/generator.py
2025-12-31 15:33:04 +01:00

127 lines
4.1 KiB
Python
Executable File

#!/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())