Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/google/adk/evaluation/_audio_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
LIVE_OUTPUT_RATE_HZ = 24000
LIVE_INPUT_MIME_TYPE = "audio/pcm;rate=16000"

_RATE_RE = re.compile(r"rate=(\d+)")
_RATE_RE = re.compile(r"(?:^|;)\s*rate\s*=\s*(\d+)\s*(?=;|$)", re.IGNORECASE)


def parse_sample_rate(mime_type: str | None, default: int) -> int:
Expand All @@ -49,6 +49,8 @@ def resample_pcm16(pcm: bytes, src_rate: int, dst_rate: int) -> bytes:
interpolate, avoiding a heavy DSP dependency for speech relayed to a
transcribing model.
"""
if src_rate <= 0 or dst_rate <= 0:
raise ValueError("Sample rates must be positive")
if not pcm or src_rate == dst_rate:
return pcm

Expand Down
25 changes: 25 additions & 0 deletions tests/unittests/evaluation/test_audio_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import logging

from google.adk.evaluation import _audio_utils as audio_utils
import pytest


def _pcm(samples: list[int]) -> bytes:
Expand Down Expand Up @@ -58,6 +59,18 @@ def test_parse_sample_rate_none_returns_default():
assert audio_utils.parse_sample_rate(None, 16000) == 16000


def test_parse_sample_rate_ignores_rate_substrings():
"""A parameter containing rate as a substring is not a sample rate."""
assert (
audio_utils.parse_sample_rate("audio/pcm;bitrate=128000", 24000) == 24000
)


def test_parse_sample_rate_is_case_insensitive():
"""The rate parameter name is parsed case-insensitively."""
assert audio_utils.parse_sample_rate("audio/pcm;RATE=16000", 24000) == 16000


# ---------------------------------------------------------------------------
# resample_pcm16
# ---------------------------------------------------------------------------
Expand All @@ -75,6 +88,18 @@ def test_resample_empty_input_returns_empty():
assert audio_utils.resample_pcm16(b"", 24000, 16000) == b""


def test_resample_zero_source_rate_raises():
"""A zero source sample rate is rejected before resampling."""
with pytest.raises(ValueError, match="Sample rates must be positive"):
audio_utils.resample_pcm16(_pcm([1, 2]), 0, 16000)


def test_resample_zero_target_rate_raises():
"""A zero target sample rate is rejected before resampling."""
with pytest.raises(ValueError, match="Sample rates must be positive"):
audio_utils.resample_pcm16(_pcm([1, 2]), 24000, 0)


def test_resample_single_sample_returns_input_unchanged():
"""Audio too short to interpolate is returned unchanged."""
pcm = _pcm([42])
Expand Down
Loading