Signed-off-by: Zhang <jianmusings@gmail.com> Signed-off-by: Zhang Jian <jianmusings@gmail.com> Signed-off-by: jian <jianmusings@gmail.com> Signed-off-by: Canlin Guo <961750412@qq.com> Co-authored-by: Canlin Guo <961750412@qq.com>
129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
"""MOSS-SoundEffect dataset for ``vllm bench serve``.
|
|
|
|
Loads ``meta.lst`` rows of the form::
|
|
|
|
utt_id|ambient_sound|duration_seconds
|
|
|
|
No reference audio. Builds one SampleRequest per row sending
|
|
``ambient_sound`` + ``duration_seconds`` to ``/v1/audio/speech``. Speaker
|
|
similarity (SIM) is not applicable; the ``seed_tts_ref_wav_path`` field is
|
|
left empty so the seed-tts-eval pipeline skips it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import random
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from vllm.benchmarks.datasets import SampleRequest
|
|
from vllm.tokenizers import TokenizerLike
|
|
from vllm.tokenizers.hf import get_cached_tokenizer
|
|
|
|
from vllm_omni.benchmarks.data_modules.seed_tts_dataset import (
|
|
SeedTTSDataset,
|
|
SeedTTSSampleRequest,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class _SoundEffectRow:
|
|
utterance_id: str
|
|
ambient_sound: str
|
|
duration_seconds: float
|
|
|
|
|
|
def _parse_se_line(line: str) -> _SoundEffectRow | None:
|
|
line = line.strip()
|
|
if not line or line.startswith("#"):
|
|
return None
|
|
parts = line.split("|")
|
|
if len(parts) < 3:
|
|
logger.warning("Skipping malformed sound-effect meta line (need 3 '|'-fields): %r", line[:120])
|
|
return None
|
|
try:
|
|
duration = float(parts[2].strip())
|
|
except ValueError:
|
|
logger.warning("Bad duration_seconds %r in: %s", parts[2], line[:120])
|
|
return None
|
|
text = parts[1].strip()
|
|
if not text:
|
|
return None
|
|
return _SoundEffectRow(utterance_id=parts[0].strip(), ambient_sound=text, duration_seconds=duration)
|
|
|
|
|
|
class SoundEffectDataset(SeedTTSDataset):
|
|
"""Ambient-sound prompts for MOSS-SoundEffect.
|
|
|
|
Reuses the SeedTTS root resolver. The ``input`` text in each request is
|
|
set to an empty string — SoundEffect ignores it; ``ambient_sound`` is the
|
|
actual conditioning signal.
|
|
"""
|
|
|
|
DEFAULT_OUTPUT_LEN = 512 # ~40 s of audio at 12.5 fps
|
|
|
|
def load_data(self) -> None:
|
|
meta = self._root / self.locale / "meta.lst"
|
|
if not meta.is_file():
|
|
raise FileNotFoundError(
|
|
f"Sound-effect meta not found: {meta} (expected {self._root}/{self.locale}/meta.lst)"
|
|
)
|
|
text = meta.read_text(encoding="utf-8")
|
|
rows: list[_SoundEffectRow] = []
|
|
for line in text.splitlines():
|
|
r = _parse_se_line(line)
|
|
if r is not None:
|
|
rows.append(r)
|
|
if not rows:
|
|
raise ValueError(f"No valid rows in {meta}")
|
|
if not self.disable_shuffle:
|
|
rng = random.Random(self.random_seed)
|
|
rng.shuffle(rows)
|
|
self._se_rows = rows
|
|
self._rows = []
|
|
self.data = self._se_rows
|
|
logger.info("Loaded SoundEffect: root=%s locale=%s rows=%d", self._root, self.locale, len(rows))
|
|
|
|
def sample(
|
|
self,
|
|
tokenizer: TokenizerLike,
|
|
num_requests: int,
|
|
output_len: int | None = None,
|
|
request_id_prefix: str = "",
|
|
no_oversample: bool = False,
|
|
**kwargs: Any,
|
|
) -> list[SampleRequest]:
|
|
if output_len is None:
|
|
output_len = self.DEFAULT_OUTPUT_LEN
|
|
tok = get_cached_tokenizer(tokenizer)
|
|
out: list[SampleRequest] = []
|
|
for i, row in enumerate(self._se_rows):
|
|
if len(out) >= num_requests:
|
|
break
|
|
speech_extra: dict[str, Any] = {
|
|
"ambient_sound": row.ambient_sound,
|
|
"duration_seconds": row.duration_seconds,
|
|
"max_new_tokens": output_len,
|
|
}
|
|
prompt_len = max(1, len(tok.encode(row.ambient_sound)))
|
|
out.append(
|
|
SeedTTSSampleRequest(
|
|
prompt="", # SoundEffect ignores text
|
|
prompt_len=prompt_len,
|
|
expected_output_len=output_len,
|
|
multi_modal_data=None,
|
|
request_id=f"{request_id_prefix}{i}",
|
|
seed_tts_speech_extra=speech_extra,
|
|
seed_tts_utterance_id=row.utterance_id,
|
|
seed_tts_locale=self.locale,
|
|
seed_tts_system_prompt=self._system_prompt,
|
|
seed_tts_ref_wav_path="", # SIM not applicable
|
|
)
|
|
)
|
|
logger.info("SoundEffect: built %d requests (asked %d)", len(out), num_requests)
|
|
self.maybe_oversample_requests(out, num_requests, request_id_prefix, no_oversample)
|
|
return out
|