Spaces:
Sleeping
Sleeping
File size: 4,425 Bytes
d9af7ee | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | """Audio playback utilities for patch visualization."""
from __future__ import annotations
from functools import lru_cache
from pathlib import Path
from typing import List, Tuple
import numpy as np
import soundfile as sf
TOKENS_PER_SECOND = 40 # WavTokenizer rate
@lru_cache(maxsize=16)
def _cached_audio(path: str, mtime: float) -> Tuple[int, np.ndarray]:
"""Read audio data from disk and memoize the decoded array."""
audio, sr = sf.read(path)
return sr, audio
def clear_audio_cache() -> None:
"""Drop all cached audio samples (useful for tests)."""
_cached_audio.cache_clear()
def _read_audio(audio_path: str) -> Tuple[int, np.ndarray]:
resolved = Path(audio_path).resolve()
stat = resolved.stat()
return _cached_audio(str(resolved), stat.st_mtime)
def load_audio_file(audio_path: str) -> Tuple[int, np.ndarray]:
"""Load audio file, reusing cached decode when possible."""
return _read_audio(audio_path)
def get_patch_audio(
audio_path: str,
patches: List[List[int]],
patch_index: int
) -> Tuple[int, np.ndarray]:
"""
Extract audio for a specific patch.
Args:
audio_path: Path to audio file
patches: List of patches
patch_index: Index of patch to extract
Returns:
(sample_rate, audio_segment)
"""
if patch_index < 0 or patch_index >= len(patches):
return 24000, np.array([])
sr, audio = _read_audio(audio_path)
patch = patches[patch_index]
if not patch:
return sr, np.array([])
# Convert token indices to audio samples
start_token = patch[0]
end_token = patch[-1]
start_sample = int(start_token / TOKENS_PER_SECOND * sr)
end_sample = int((end_token + 1) / TOKENS_PER_SECOND * sr)
# Clamp to valid range
start_sample = max(0, start_sample)
end_sample = min(len(audio), end_sample)
segment = audio[start_sample:end_sample]
return sr, segment
def play_until_boundary(
audio_path: str,
patches: List[List[int]],
current_token: int
) -> Tuple[int, np.ndarray]:
"""
Get audio from current token position until next patch boundary.
Args:
audio_path: Path to audio file
patches: List of patches
current_token: Current token position
Returns:
(sample_rate, audio_segment)
"""
# Find which patch contains current token
current_patch_idx = None
for i, patch in enumerate(patches):
if current_token in patch:
current_patch_idx = i
break
if current_patch_idx is None:
# Token not in any patch, return empty
return 24000, np.array([])
# Get audio for this patch from current position onward
sr, audio = _read_audio(audio_path)
patch = patches[current_patch_idx]
start_token = current_token
end_token = patch[-1]
# Convert to audio samples
start_sample = int(start_token / TOKENS_PER_SECOND * sr)
end_sample = int((end_token + 1) / TOKENS_PER_SECOND * sr)
# Clamp to valid range
start_sample = max(0, start_sample)
end_sample = min(len(audio), end_sample)
segment = audio[start_sample:end_sample]
return sr, segment
def find_patch_for_token(patches: List[List[int]], token_idx: int) -> int:
"""
Find which patch contains a given token.
Args:
patches: List of patches
token_idx: Token index
Returns:
Patch index, or -1 if not found
"""
for i, patch in enumerate(patches):
if token_idx in patch:
return i
return -1
def get_patch_info(
patches: List[List[int]],
patch_idx: int
) -> dict:
"""
Get information about a specific patch.
Args:
patches: List of patches
patch_idx: Patch index
Returns:
Dictionary with patch info
"""
if patch_idx < 0 or patch_idx >= len(patches):
return {}
patch = patches[patch_idx]
start_token = patch[0]
end_token = patch[-1]
num_tokens = len(patch)
start_time = start_token / TOKENS_PER_SECOND
end_time = (end_token + 1) / TOKENS_PER_SECOND
duration = num_tokens / TOKENS_PER_SECOND
return {
"patch_index": patch_idx,
"start_token": start_token,
"end_token": end_token,
"num_tokens": num_tokens,
"start_time_sec": start_time,
"end_time_sec": end_time,
"duration_sec": duration,
}
|