4337a31b80
Major improvements to GUI stability and internationalization: - Fix GUI freezing by implementing threaded audio analysis - Add AudioAnalysisWorker thread for background processing - Progress signals with percentage updates - Thread-safe communication via Qt signals - Add comprehensive CLI logging system - 5 log levels: ERROR, WARN, INFO, DEBUG, TRACE - Command line control: --log-level, --log-file - Real-time feedback during analysis operations - Implement CJK font fallback system - FontManager with 3-tier fallback (custom → system → default) - Cross-platform CJK font detection (Windows/macOS/Linux) - Licensing-safe fonts/ directory with gitignored font files - Setup utility and comprehensive documentation - Fix numpy array formatting issue with BPM detection - Add progress indicators for long-running operations - Preserve fonts directory structure with placeholder file 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
80 lines
2.5 KiB
Python
80 lines
2.5 KiB
Python
"""
|
|
Audio visualization plotting engine.
|
|
Separates plotting logic from audio processing for clean GUI integration.
|
|
"""
|
|
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.colors as mcolors
|
|
import matplotlib.cm as cm
|
|
from matplotlib.figure import Figure
|
|
import os
|
|
from font_manager import safe_title
|
|
|
|
|
|
class PlottingEngine:
|
|
"""Handles all matplotlib visualization logic for audio analysis."""
|
|
|
|
@staticmethod
|
|
def create_power_analysis_figure(times, rms_array, file_path, figsize=(10, 4)):
|
|
"""
|
|
Creates a matplotlib Figure for power analysis visualization.
|
|
|
|
Args:
|
|
times: Array of time points
|
|
rms_array: RMS power values over time
|
|
file_path: Path to the audio file for title
|
|
figsize: Figure size tuple
|
|
|
|
Returns:
|
|
matplotlib.figure.Figure: Ready-to-embed figure
|
|
"""
|
|
# Determine color scale based on headroom detection
|
|
local_max_power = np.max(rms_array)
|
|
if local_max_power > 0.3:
|
|
norm = mcolors.Normalize(vmin=0, vmax=0.6)
|
|
maxpower = 0.6
|
|
else:
|
|
norm = mcolors.Normalize(vmin=0, vmax=0.3)
|
|
maxpower = 0.3
|
|
|
|
# Create figure and axis
|
|
fig = Figure(figsize=figsize, facecolor='white')
|
|
ax = fig.add_subplot(111)
|
|
|
|
# Color map
|
|
cmap = cm.autumn
|
|
|
|
# Plot power levels as colored bars
|
|
ax.set_ylim(0., maxpower)
|
|
for i in range(len(times)-1):
|
|
ax.fill_between(times[i:i+2], 0, rms_array[0][i],
|
|
color=cmap(norm(rms_array[0][i])), edgecolor='none')
|
|
|
|
# Add colorbar
|
|
sm = cm.ScalarMappable(cmap=cmap, norm=norm)
|
|
sm.set_array([])
|
|
cbar = fig.colorbar(sm, ax=ax, label='RMS Power')
|
|
|
|
# Labels and title
|
|
ax.set_ylabel('Power')
|
|
ax.set_xlabel('Time (seconds)')
|
|
ax.set_title(safe_title(os.path.basename(file_path)))
|
|
|
|
# Tight layout for better appearance in GUI
|
|
fig.tight_layout()
|
|
|
|
return fig
|
|
|
|
@staticmethod
|
|
def create_metadata_display_text(song_name, bpm, max_amplitude, avg_amplitude):
|
|
"""
|
|
Creates formatted text for metadata display.
|
|
|
|
Returns:
|
|
str: Formatted metadata text
|
|
"""
|
|
return f"""Track: {safe_title(song_name)}
|
|
BPM: {bpm:.1f}
|
|
Max Amplitude: {max_amplitude:.3f}
|
|
Avg Amplitude: {avg_amplitude:.3f}""" |