Skip to content

Commit 0a4eebb

Browse files
authored
add require_audio
1 parent e50656f commit 0a4eebb

8 files changed

Lines changed: 39 additions & 37 deletions

File tree

‎src/transformers/audio_utils.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ def _resolve_audio_source(audio: str, timeout: float | None = None) -> "str | by
196196
def _load_audio_soundfile(source: "str | bytes", sampling_rate: int) -> np.ndarray:
197197
"""Decode with soundfile and resample with soxr: the same steps as `librosa.load`, so the output is identical
198198
(downmix by averaging, `soxr_hq` resampling, padding/trimming to `ceil(num_samples * sampling_rate / src_rate)`)."""
199-
requires_backends(load_audio, ["soundfile", "soxr"])
199+
requires_backends(load_audio, ["audio"])
200200
with sf.SoundFile(BytesIO(source) if isinstance(source, bytes) else source) as f:
201201
src_sampling_rate = f.samplerate
202202
waveform = f.read(dtype="float32", always_2d=True).T

‎src/transformers/testing_utils.py‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
is_apex_available,
8181
is_apollo_torch_available,
8282
is_aqlm_available,
83+
is_audio_available,
8384
is_auto_round_available,
8485
is_av_available,
8586
is_bitsandbytes_available,
@@ -153,7 +154,6 @@
153154
is_seqio_available,
154155
is_serve_available,
155156
is_soundfile_available,
156-
is_soxr_available,
157157
is_spacy_available,
158158
is_speech_available,
159159
is_spqr_available,
@@ -887,6 +887,14 @@ def require_torchcodec(test_case):
887887
return unittest.skipUnless(is_torchcodec_available(), "test requires Torchcodec")(test_case)
888888

889889

890+
def require_audio(test_case):
891+
"""
892+
Decorator marking a test that requires the audio dependencies (soundfile and soxr, installed with
893+
`transformers[audio]`). These tests are skipped when they aren't installed.
894+
"""
895+
return unittest.skipUnless(is_audio_available(), "test requires audio")(test_case)
896+
897+
890898
def require_torchaudio(test_case):
891899
"""
892900
Decorator marking a test that requires torchaudio. These tests are skipped when torchaudio isn't installed.
@@ -1828,13 +1836,6 @@ def require_soundfile(test_case):
18281836
return unittest.skipUnless(is_soundfile_available(), "test requires soundfile")(test_case)
18291837

18301838

1831-
def require_soxr(test_case):
1832-
"""
1833-
Decorator marking a test that requires soxr
1834-
"""
1835-
return unittest.skipUnless(is_soxr_available(), "test requires soxr")(test_case)
1836-
1837-
18381839
def require_multipart(test_case):
18391840
"""
18401841
Decorator marking a test that requires python-multipart

‎src/transformers/utils/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
is_apex_available,
120120
is_apollo_torch_available,
121121
is_aqlm_available,
122+
is_audio_available,
122123
is_auto_round_available,
123124
is_av_available,
124125
is_bitsandbytes_available,

‎src/transformers/utils/import_utils.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,13 @@ def is_soxr_available() -> bool:
16621662
return _is_package_available("soxr")[0]
16631663

16641664

1665+
@lru_cache
1666+
@_make_compile_constant
1667+
def is_audio_available() -> bool:
1668+
"""The audio core's dependencies, installed with `transformers[audio]`: soundfile to decode, soxr to resample."""
1669+
return is_soundfile_available() and is_soxr_available()
1670+
1671+
16651672
@lru_cache
16661673
@_make_compile_constant
16671674
def is_speech_available() -> bool:
@@ -2052,6 +2059,12 @@ def is_training_run_on_sagemaker() -> bool:
20522059
return "SAGEMAKER_JOB_NAME" in os.environ
20532060

20542061

2062+
# docstyle-ignore
2063+
AUDIO_IMPORT_ERROR = """
2064+
{0} requires soundfile and soxr, which were not found in your environment. You can install them with pip:
2065+
`pip install transformers[audio]`. Please note that you may need to restart your runtime after installation.
2066+
"""
2067+
20552068
# docstyle-ignore
20562069
AV_IMPORT_ERROR = """
20572070
{0} requires the PyAv library but it was not found in your environment. You can install it with:
@@ -2407,6 +2420,7 @@ def is_training_run_on_sagemaker() -> bool:
24072420

24082421
BACKENDS_MAPPING = OrderedDict(
24092422
[
2423+
("audio", (is_audio_available, AUDIO_IMPORT_ERROR)),
24102424
("av", (is_av_available, AV_IMPORT_ERROR)),
24112425
("bs4", (is_bs4_available, BS4_IMPORT_ERROR)),
24122426
("cv2", (is_cv2_available, CV2_IMPORT_ERROR)),

‎tests/cli/test_serve.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,9 @@
4848
response_events_to_chunks,
4949
)
5050
from transformers.testing_utils import (
51+
require_audio,
5152
require_multipart,
5253
require_serve,
53-
require_soundfile,
54-
require_soxr,
5554
require_torch,
5655
require_torch_accelerator,
5756
require_torchcodec,
@@ -2733,8 +2732,7 @@ def serve_kwargs(cls) -> dict:
27332732

27342733

27352734
@slow
2736-
@require_soundfile
2737-
@require_soxr
2735+
@require_audio
27382736
@require_multipart
27392737
@require_serve
27402738
class TestTranscription(ServeIntegrationTestCase):

‎tests/models/muse_spark/test_modeling_muse_spark.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@
2929
from transformers.models.muse_spark.configuration_muse_spark import MuseSparkTextConfig, MuseSparkVisionConfig
3030
from transformers.testing_utils import (
3131
Expectations,
32+
require_audio,
3233
require_deterministic_for_xpu,
33-
require_librosa,
34-
require_soundfile,
3534
require_torch,
3635
require_torch_accelerator,
3736
require_torch_accelerator_memory,
@@ -536,8 +535,7 @@ def test_integration_video(self):
536535
)
537536
self.assertIn(EXPECTED_TEXT_2.get_expectation(), message["content"])
538537

539-
@require_librosa
540-
@require_soundfile
538+
@require_audio
541539
def test_integration_audio(self):
542540
messages = [
543541
{

‎tests/pipelines/test_pipelines_any_to_any.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
CaptureLogger,
3131
Expectations,
3232
is_pipeline_test,
33-
require_soundfile,
34-
require_soxr,
33+
require_audio,
3534
require_torch,
3635
require_vision,
3736
slow,
@@ -53,8 +52,7 @@
5352

5453
@is_pipeline_test
5554
@require_vision
56-
@require_soundfile
57-
@require_soxr
55+
@require_audio
5856
@require_torch
5957
class AnyToAnyPipelineTests(unittest.TestCase):
6058
model_mapping = MODEL_FOR_MULTIMODAL_LM_MAPPING

‎tests/utils/test_audio_utils.py‎

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@
4545
)
4646
from transformers.testing_utils import (
4747
is_librosa_available,
48+
require_audio,
4849
require_librosa,
49-
require_soundfile,
50-
require_soxr,
5150
require_torchaudio,
5251
require_torchcodec,
5352
slow,
@@ -1911,8 +1910,7 @@ def test_format_from_source(self):
19111910

19121911
# ---- source forms: local path, URL, base64, data: URI -------------------------------------
19131912

1914-
@require_soundfile
1915-
@require_soxr
1913+
@require_audio
19161914
def test_load_audio_from_every_source_form(self):
19171915
# a single sync gives us the file as a local path, a public URL, raw base64 and a data: URI
19181916
with patch("transformers.audio_utils.is_torchcodec_usable", return_value=False):
@@ -1921,8 +1919,7 @@ def test_load_audio_from_every_source_form(self):
19211919
self.assertIsInstance(audio, np.ndarray, form)
19221920
self.assertGreater(audio.size, 0, form)
19231921

1924-
@require_soundfile
1925-
@require_soxr
1922+
@require_audio
19261923
def test_invalid_base64_raises_value_error(self):
19271924
# a non-URL, non-path string is treated as base64 and must fail cleanly
19281925
with patch("transformers.audio_utils.is_torchcodec_usable", return_value=False):
@@ -1932,8 +1929,7 @@ def test_invalid_base64_raises_value_error(self):
19321929

19331930
# ---- soundfile fallback (torchcodec unavailable) ------------------------------------------
19341931

1935-
@require_soundfile
1936-
@require_soxr
1932+
@require_audio
19371933
def test_soundfile_decodes_soundfile_formats(self):
19381934
with patch("transformers.audio_utils.is_torchcodec_usable", return_value=False):
19391935
for name, filetype in self._BUCKET_FILETYPES.items():
@@ -1944,8 +1940,7 @@ def test_soundfile_decodes_soundfile_formats(self):
19441940
self.assertGreater(audio.size, 0, name)
19451941

19461942
@require_librosa
1947-
@require_soundfile
1948-
@require_soxr
1943+
@require_audio
19491944
def test_soundfile_matches_librosa_on_soundfile_formats(self):
19501945
# the soundfile backend replaces `librosa.load`, so it must return exactly the same samples
19511946
for name, filetype in self._BUCKET_FILETYPES.items():
@@ -1955,8 +1950,7 @@ def test_soundfile_matches_librosa_on_soundfile_formats(self):
19551950
expected = librosa.load(self._path(name), sr=16000)[0]
19561951
np.testing.assert_array_equal(audio, expected, err_msg=name)
19571952

1958-
@require_soundfile
1959-
@require_soxr
1953+
@require_audio
19601954
def test_soundfile_rejects_torchcodec_only_formats_with_helpful_error(self):
19611955
with patch("transformers.audio_utils.is_torchcodec_usable", return_value=False):
19621956
for name, filetype in self._BUCKET_FILETYPES.items():
@@ -1980,8 +1974,7 @@ def test_video_url_rejected_up_front_without_download(self):
19801974
self.assertIn("torchcodec", message)
19811975
fetch.assert_not_called()
19821976

1983-
@require_soundfile
1984-
@require_soxr
1977+
@require_audio
19851978
def test_deprecated_load_audio_librosa_warns_and_pins_soundfile(self):
19861979
# BC: even with torchcodec available, the alias must still decode with soundfile (what librosa used)
19871980
with patch("transformers.audio_utils.is_torchcodec_usable", return_value=True):
@@ -2026,8 +2019,7 @@ def test_unknown_backend_raises(self):
20262019
load_audio(self._path("audio.wav"), sampling_rate=16000, backend="not_a_backend")
20272020

20282021

2029-
@require_soundfile
2030-
@require_soxr
2022+
@require_audio
20312023
class LoadAudioSoundfileTester(unittest.TestCase):
20322024
"""The `"soundfile"` backend replaces `librosa.load`: same samples, without librosa."""
20332025

0 commit comments

Comments
 (0)