75 lines
2.2 KiB
Python
75 lines
2.2 KiB
Python
# Copyright 2026 Umbra Tools (umbratools.com)
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class AudioGenerator:
|
|
def __init__(self, tts_engine: Any, quality_checker: Any, config: Any) -> None:
|
|
self.tts = tts_engine
|
|
self.quality = quality_checker
|
|
self.config = config
|
|
|
|
async def generate_utterance(
|
|
self,
|
|
utterance: dict[str, Any],
|
|
output_path: Path,
|
|
) -> dict[str, Any]:
|
|
voice_ref = utterance.get("voice_ref")
|
|
instruct = utterance.get("instruct")
|
|
text = utterance["text"]
|
|
|
|
try:
|
|
result_path = self.tts.generate(
|
|
text=text,
|
|
output_path=output_path,
|
|
voice_ref=Path(voice_ref) if voice_ref else None,
|
|
instruct=instruct,
|
|
)
|
|
|
|
quality = self.quality.check_audio_quality(result_path)
|
|
|
|
return {
|
|
"status": "generated",
|
|
"path": str(result_path),
|
|
"quality": quality,
|
|
}
|
|
except Exception as e:
|
|
logger.error("Generation failed for utterance %s: %s", utterance.get("id"), e)
|
|
return {
|
|
"status": "failed",
|
|
"error": str(e),
|
|
}
|
|
|
|
async def generate_chapter(
|
|
self,
|
|
manifest: dict[str, Any],
|
|
output_dir: Path,
|
|
) -> dict[str, Any]:
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
results = []
|
|
|
|
for utterance in manifest.get("utterances", []):
|
|
utt_id = utterance["id"]
|
|
out_path = output_dir / f"{utt_id}.wav"
|
|
|
|
result = await self.generate_utterance(utterance, out_path)
|
|
results.append(result)
|
|
|
|
successful = sum(1 for r in results if r["status"] == "generated")
|
|
failed = sum(1 for r in results if r["status"] == "failed")
|
|
|
|
return {
|
|
"chapter_id": manifest.get("chapter_id"),
|
|
"total_utterances": len(results),
|
|
"successful": successful,
|
|
"failed": failed,
|
|
"results": results,
|
|
}
|