Will show average amplitude in the widget

This commit is contained in:
Raivis Dejus 2026-02-22 11:52:29 +02:00
commit 9508103420
4 changed files with 42 additions and 9 deletions

View file

@ -9,6 +9,9 @@ from PyQt6.QtCore import QObject, pyqtSignal
class RecordingAmplitudeListener(QObject):
stream: Optional[sounddevice.InputStream] = None
amplitude_changed = pyqtSignal(float)
average_amplitude_changed = pyqtSignal(float)
ACCUMULATION_SECONDS = 1
def __init__(
self,
@ -17,6 +20,8 @@ class RecordingAmplitudeListener(QObject):
):
super().__init__(parent)
self.input_device_index = input_device_index
self.buffer = np.ndarray([], dtype=np.float32)
self.accumulation_size = 0
def start_recording(self):
try:
@ -27,6 +32,7 @@ class RecordingAmplitudeListener(QObject):
callback=self.stream_callback,
)
self.stream.start()
self.accumulation_size = int(self.stream.samplerate * self.ACCUMULATION_SECONDS)
except Exception as e:
self.stop_recording()
logging.exception("Failed to start audio stream on device %s: %s", self.input_device_index, e)
@ -38,5 +44,9 @@ class RecordingAmplitudeListener(QObject):
def stream_callback(self, in_data: np.ndarray, frame_count, time_info, status):
chunk = in_data.ravel()
amplitude = np.sqrt(np.mean(chunk**2)) # root-mean-square
self.amplitude_changed.emit(amplitude)
self.amplitude_changed.emit(float(np.sqrt(np.mean(chunk**2))))
self.buffer = np.append(self.buffer, chunk)
if self.buffer.size >= self.accumulation_size:
self.average_amplitude_changed.emit(float(np.sqrt(np.mean(self.buffer**2))))
self.buffer = np.ndarray([], dtype=np.float32)

View file

@ -39,6 +39,7 @@ class RecordingTranscriber(QObject):
finished = pyqtSignal()
error = pyqtSignal(str)
amplitude_changed = pyqtSignal(float)
average_amplitude_changed = pyqtSignal(float)
is_running = False
SAMPLE_RATE = whisper_audio.SAMPLE_RATE
@ -180,6 +181,7 @@ class RecordingTranscriber(QObject):
self.mutex.release()
amplitude = self.amplitude(samples)
self.average_amplitude_changed.emit(amplitude)
logging.debug(
"Processing next frame, sample size = %s, queue size = %s, amplitude = %s",
@ -188,7 +190,7 @@ class RecordingTranscriber(QObject):
amplitude,
)
if amplitude < 0.025:
if amplitude < 0.0025:
time.sleep(0.5)
continue
@ -351,7 +353,7 @@ class RecordingTranscriber(QObject):
# Try to enqueue the next block. If the queue is already full, drop the block.
chunk: np.ndarray = in_data.ravel()
amplitude = float(np.sqrt(np.mean(chunk**2)))
amplitude = self.amplitude(chunk)
self.amplitude_changed.emit(amplitude)
with self.mutex:
@ -360,7 +362,7 @@ class RecordingTranscriber(QObject):
@staticmethod
def amplitude(arr: np.ndarray):
return (abs(max(arr)) + abs(min(arr))) / 2
return float(np.sqrt(np.mean(arr**2)))
def _drain_stderr(self):
if self.process and self.process.stderr:

View file

@ -1,7 +1,7 @@
from typing import Optional
from PyQt6 import QtGui
from PyQt6.QtCore import Qt
from PyQt6.QtCore import Qt, QRect
from PyQt6.QtGui import QColor, QPainter
from PyQt6.QtWidgets import QWidget
@ -20,13 +20,16 @@ class AudioMeterWidget(QWidget):
def __init__(self, parent: Optional[QWidget] = None):
super().__init__(parent)
self.setMinimumWidth(10)
self.setFixedHeight(16)
self.setFixedHeight(30)
self.BARS_HEIGHT = 14
# Extra padding to fix layout
self.PADDING_TOP = 3
self.current_amplitude = 0.0
self.average_amplitude = 0.0
self.MINIMUM_AMPLITUDE = 0.00005 # minimum amplitude to show the first bar
self.AMPLITUDE_SCALE_FACTOR = 10 # scale the amplitudes such that 1/AMPLITUDE_SCALE_FACTOR will show all bars
@ -58,18 +61,23 @@ class AudioMeterWidget(QWidget):
center_x - ((i + 1) * (self.BAR_MARGIN + self.BAR_WIDTH)),
rect.top() + self.PADDING_TOP,
self.BAR_WIDTH,
rect.height() - self.PADDING_TOP,
self.BARS_HEIGHT - self.PADDING_TOP,
)
# draw to right
painter.drawRect(
center_x + (self.BAR_MARGIN + (i * (self.BAR_MARGIN + self.BAR_WIDTH))),
rect.top() + self.PADDING_TOP,
self.BAR_WIDTH,
rect.height() - self.PADDING_TOP,
self.BARS_HEIGHT - self.PADDING_TOP,
)
text_rect = QRect(rect.left(), self.BARS_HEIGHT, rect.width(), rect.height() - self.BARS_HEIGHT)
painter.setPen(self.BAR_ACTIVE_COLOR)
painter.drawText(text_rect, Qt.AlignmentFlag.AlignCenter, f"{self.average_amplitude:.4f}")
def reset_amplitude(self):
self.current_amplitude = 0.0
self.average_amplitude = 0.0
self.repaint()
def update_amplitude(self, amplitude: float):
@ -77,3 +85,7 @@ class AudioMeterWidget(QWidget):
amplitude, self.current_amplitude * self.SMOOTHING_FACTOR
)
self.update()
def update_average_amplitude(self, amplitude: float):
self.average_amplitude = amplitude
self.update()

View file

@ -509,6 +509,9 @@ class RecordingTranscriberWidget(QWidget):
self.recording_amplitude_listener.amplitude_changed.connect(
self.on_recording_amplitude_changed, Qt.ConnectionType.QueuedConnection
)
self.recording_amplitude_listener.average_amplitude_changed.connect(
self.audio_meter_widget.update_average_amplitude, Qt.ConnectionType.QueuedConnection
)
self.recording_amplitude_listener.start_recording()
def on_record_button_clicked(self):
@ -519,6 +522,9 @@ class RecordingTranscriberWidget(QWidget):
self.recording_amplitude_listener.amplitude_changed.disconnect(
self.on_recording_amplitude_changed
)
self.recording_amplitude_listener.average_amplitude_changed.disconnect(
self.audio_meter_widget.update_average_amplitude
)
self.recording_amplitude_listener.stop_recording()
self.recording_amplitude_listener = None
self.audio_meter_widget.reset_amplitude()
@ -587,6 +593,9 @@ class RecordingTranscriberWidget(QWidget):
self.transcriber.amplitude_changed.connect(
self.on_recording_amplitude_changed, Qt.ConnectionType.QueuedConnection
)
self.transcriber.average_amplitude_changed.connect(
self.audio_meter_widget.update_average_amplitude, Qt.ConnectionType.QueuedConnection
)
# Stop the separate amplitude listener to avoid two streams on the same device
if self.recording_amplitude_listener is not None: