fix: support uint8 websocket audio format

This commit is contained in:
nightcityblade
2026-06-02 11:13:24 +08:00
committed by Aaron Boxer
parent 582d5426d6
commit 32c1b18c9f
3 changed files with 23 additions and 3 deletions
+1 -1
View File
@@ -257,7 +257,7 @@ Accept raw PCM int16 audio from clients (useful for embedded devices):
```bash ```bash
python3 run_server.py --port 9090 --backend faster_whisper --raw_pcm_input python3 run_server.py --port 9090 --backend faster_whisper --raw_pcm_input
``` ```
Audio is automatically normalized to float32 range [-1.0, 1.0]. Audio is automatically normalized to float32 range [-1.0, 1.0]. Clients can also set `audio_format` in the initial websocket options to `float32` (default), `int16`, or `uint8`.
## Browser Extensions ## Browser Extensions
- Run the server with your desired backend as shown [here](https://github.com/collabora/WhisperLive?tab=readme-ov-file#running-the-server). - Run the server with your desired backend as shown [here](https://github.com/collabora/WhisperLive?tab=readme-ov-file#running-the-server).
+10
View File
@@ -273,6 +273,16 @@ class TestTranscriptionServerGetAudio(unittest.TestCase):
self.assertTrue(np.all(result >= -1.0)) self.assertTrue(np.all(result >= -1.0))
self.assertTrue(np.all(result <= 1.0)) self.assertTrue(np.all(result <= 1.0))
def test_uint8_audio_format_normalizes_unsigned_pcm(self):
import numpy as np
ws = MagicMock()
self.server.audio_formats[ws] = "uint8"
pcm = np.array([0, 128, 255], dtype=np.uint8)
ws.recv.return_value = pcm.tobytes()
result = self.server.get_audio_from_websocket(ws)
expected = (pcm.astype(np.float32) - 128.0) / 128.0
np.testing.assert_array_almost_equal(result, expected)
def test_raw_pcm_input_off_reads_float32(self): def test_raw_pcm_input_off_reads_float32(self):
import numpy as np import numpy as np
self.server.raw_pcm_input = False self.server.raw_pcm_input = False
+11 -1
View File
@@ -178,6 +178,7 @@ class TranscriptionServer:
self.single_model = False self.single_model = False
self.batch_config = None self.batch_config = None
self.raw_pcm_input = False self.raw_pcm_input = False
self.audio_formats = {}
self.segment_post_processor = None self.segment_post_processor = None
def initialize_client( def initialize_client(
@@ -361,7 +362,11 @@ class TranscriptionServer:
frame_data = websocket.recv() frame_data = websocket.recv()
if frame_data == b"END_OF_AUDIO": if frame_data == b"END_OF_AUDIO":
return False return False
if self.raw_pcm_input: audio_format = self.audio_formats.get(websocket)
if audio_format == "uint8":
audio_np = np.frombuffer(frame_data, dtype=np.uint8)
return (audio_np.astype(np.float32) - 128.0) / 128.0
if self.raw_pcm_input or audio_format == "int16":
audio_np = np.frombuffer(frame_data, dtype=np.int16) audio_np = np.frombuffer(frame_data, dtype=np.int16)
return audio_np.astype(np.float32) / 32768.0 return audio_np.astype(np.float32) / 32768.0
return np.frombuffer(frame_data, dtype=np.float32) return np.frombuffer(frame_data, dtype=np.float32)
@@ -378,6 +383,10 @@ class TranscriptionServer:
wl_metrics.track_connection_rejected(reason="full") wl_metrics.track_connection_rejected(reason="full")
websocket.close() websocket.close()
return False # Indicates that the connection should not continue return False # Indicates that the connection should not continue
audio_format = options.get("audio_format", "float32")
if audio_format not in {"float32", "int16", "uint8"}:
raise ValueError(f"Unsupported audio_format: {audio_format}")
self.audio_formats[websocket] = audio_format
if self.backend.is_tensorrt(): if self.backend.is_tensorrt():
self.vad_detector = VoiceActivityDetector(frame_rate=self.RATE) self.vad_detector = VoiceActivityDetector(frame_rate=self.RATE)
@@ -845,3 +854,4 @@ class TranscriptionServer:
if hasattr(client, 'translation_thread') and client.translation_thread: if hasattr(client, 'translation_thread') and client.translation_thread:
client.translation_thread.join(timeout=2.0) client.translation_thread.join(timeout=2.0)
self.client_manager.remove_client(websocket) self.client_manager.remove_client(websocket)
self.audio_formats.pop(websocket, None)