diff --git a/audio_visualizer/src/audio_visualizer/core.py b/audio_visualizer/src/audio_visualizer/core.py new file mode 100644 index 0000000..fe3b188 --- /dev/null +++ b/audio_visualizer/src/audio_visualizer/core.py @@ -0,0 +1,87 @@ +from __future__ import annotations +import logging +from pathlib import Path +from typing import Optional + +import numpy as np +import matplotlib.pyplot as plt + +from audio_visualizer.io_utils import load_audio_data + + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) +if not logger.handlers: + handler = logging.StreamHandler() + formatter = logging.Formatter('[%(levelname)s] %(asctime)s - %(message)s') + handler.setFormatter(formatter) + logger.addHandler(handler) + + +class Visualizer: + """Verwaltet die Darstellung und Aktualisierung der Visualisierung des Frequenzspektrums.""" + + def __init__(self, output_path: Optional[str] = None) -> None: + self.output_path = Path(output_path) if output_path else None + + def render(self, spectrum: np.ndarray) -> None: + """Rendert das aktuelle Spektrum als grafische Darstellung.""" + logger.info("Starte Rendering des Spektrums …") + if spectrum.ndim != 1: + raise ValueError("Spektrum muss eindimensional sein.") + + plt.figure(figsize=(10, 6)) + plt.plot(spectrum, color='royalblue', linewidth=1.2) + plt.title('Frequenzspektrum – Echo der Wellen') + plt.xlabel('Frequenzindex') + plt.ylabel('Amplitude') + plt.grid(True, linestyle='--', alpha=0.5) + + if self.output_path: + self.output_path.parent.mkdir(parents=True, exist_ok=True) + plt.savefig(self.output_path, dpi=150) + logger.info(f"Visualisierung gespeichert unter: {self.output_path}") + else: + plt.show() + + plt.close() + logger.info("Rendering abgeschlossen.") + + +def compute_spectrum(data: np.ndarray) -> np.ndarray: + """Berechnet die Frequenzspektralanalyse aus Rohdaten.""" + logger.info("Berechne Spektrum …") + + if not isinstance(data, np.ndarray): + raise TypeError("Eingabedaten müssen ein numpy.ndarray sein.") + if data.size == 0: + raise ValueError("Eingabedaten sind leer.") + + # Überprüfung auf NaNs + if np.isnan(data).any(): + logger.warning("Eingabedaten enthalten NaN-Werte; werden durch 0 ersetzt.") + data = np.nan_to_num(data) + + spectrum = np.abs(np.fft.rfft(data - np.mean(data))) + logger.debug(f"Spektrum berechnet, Länge: {len(spectrum)}") + return spectrum + + +def generate_visualization(audio_file: str, output_file: Optional[str] = None) -> None: + """Erzeugt eine visuelle Darstellung der Audiofrequenzen aus einer Eingabedatei.""" + logger.info(f"Starte Visualisierung für Datei: {audio_file}") + + input_path = Path(audio_file) + assert input_path.exists(), f"Eingabedatei existiert nicht: {audio_file}" + + # Eingabedaten laden + data = load_audio_data(str(input_path)) + assert isinstance(data, np.ndarray) and data.size > 0, "Geladene Daten sind ungültig oder leer." + + # Spektrum berechnen + spectrum = compute_spectrum(data) + + # Visualisierung ausgeben + visualizer = Visualizer(output_file) + visualizer.render(spectrum) + logger.info("Visualisierung erfolgreich abgeschlossen.")