76 lines
2.6 KiB
Python
76 lines
2.6 KiB
Python
import numpy as np
|
|
from PyQt5 import QtWidgets
|
|
from utils import FFTAnalyser
|
|
|
|
|
|
class AudioVisualizer(QtWidgets.QWidget):
|
|
"""Audio Visualizer component"""
|
|
|
|
def __init__(self, media_player, x_resolution):
|
|
super().__init__()
|
|
self.media_player = media_player
|
|
self.x_resolution = x_resolution
|
|
self.fft_analyser = FFTAnalyser(self.media_player, self.x_resolution)
|
|
self.fft_analyser.calculated_visual.connect(self.set_amplitudes)
|
|
self.fft_analyser.start()
|
|
self.amps = np.array([])
|
|
self._plot_item = None
|
|
self._x_data = np.arange(self.x_resolution)
|
|
self.use_decibels = True # Set to True to use decibel scale
|
|
|
|
# Generate logarithmic frequency scale (20Hz - 20kHz)
|
|
self.min_freq = 20
|
|
self.max_freq = 23000
|
|
self.frequency_values = np.logspace(
|
|
np.log10(self.min_freq), np.log10(self.max_freq), self.x_resolution
|
|
)
|
|
|
|
def get_frequency_ticks(self):
|
|
"""Returns frequency ticks for x-axis display
|
|
|
|
Returns:
|
|
list: List of tuples with (position, label) for each tick
|
|
"""
|
|
# Standard frequency bands for audio visualization
|
|
standard_freqs = [20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000]
|
|
|
|
# Map frequencies to x-axis positions
|
|
ticks = []
|
|
for freq in standard_freqs:
|
|
# Find closest index to this frequency
|
|
idx = np.argmin(np.abs(self.frequency_values - freq))
|
|
# Format labels: Hz for <1000, kHz for >=1000
|
|
if freq < 1000:
|
|
label = f"{freq}Hz"
|
|
else:
|
|
label = f"{freq / 1000:.0f}kHz"
|
|
ticks.append((idx, label))
|
|
|
|
return ticks
|
|
|
|
def get_frequencies(self):
|
|
"""Return the frequency values for x-axis"""
|
|
return self.frequency_values
|
|
|
|
def get_amplitudes(self):
|
|
return self.amps
|
|
|
|
def get_decibels(self):
|
|
"""Convert amplitude values to decibel scale
|
|
|
|
Formula: dB = 20 * log10(amplitude)
|
|
For normalized amplitude values, this gives a range of approx -96dB to 0dB
|
|
With a noise floor cutoff at around -96dB (for very small values)
|
|
"""
|
|
# Avoid log(0) by adding a small epsilon
|
|
epsilon = 1e-10
|
|
amplitudes = np.maximum(self.amps, epsilon)
|
|
# Convert to decibels (20*log10 is the standard formula for amplitude to dB)
|
|
db_values = 20 * np.log10(amplitudes)
|
|
# Clip very low values to have a reasonable floor (e.g. -96dB)
|
|
db_values = np.maximum(db_values, -96)
|
|
return db_values
|
|
|
|
def set_amplitudes(self, amps):
|
|
self.amps = np.array(amps)
|