File size: 2,885 Bytes
804ee23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from __future__ import annotations

import torch
import torch.nn.functional as F

DEFAULT_EDGE_SILENCE_MS = 250.0
DEFAULT_EDGE_SILENCE_TOP_DB = 30.0


def align_length(num_samples: int, multiple_of: int | None) -> int:
    if multiple_of is None or multiple_of <= 0:
        return int(num_samples)
    if num_samples % multiple_of == 0:
        return int(num_samples)
    return int(((num_samples + multiple_of - 1) // multiple_of) * multiple_of)


def pad_waveform_align_only(
    waveform: torch.Tensor,
    *,
    multiple_of: int | None,
) -> torch.Tensor:
    if multiple_of is None or multiple_of <= 0:
        return waveform

    target_length = align_length(waveform.size(-1), multiple_of)
    delta = target_length - waveform.size(-1)
    if delta <= 0:
        return waveform

    return F.pad(waveform, (0, delta), "constant", 0.0)


def normalize_edge_silence_duration(
    waveform: torch.Tensor,
    *,
    sample_rate: int,
    target_silence_duration_ms: float = DEFAULT_EDGE_SILENCE_MS,
    top_db: float = DEFAULT_EDGE_SILENCE_TOP_DB,
) -> torch.Tensor:
    mono_waveform = waveform[0]
    target_samples = int(round(float(sample_rate) * float(target_silence_duration_ms) / 1000.0))
    amplitude = mono_waveform.abs()
    peak = float(amplitude.max().item())
    if peak <= 0.0:
        waveform = waveform[..., :target_samples]
        current_length = int(waveform.size(-1))
        if current_length < target_samples:
            waveform = F.pad(waveform, (0, target_samples - current_length), "constant", 0.0)
        return waveform

    threshold = peak * (10.0 ** (-float(top_db) / 20.0))
    non_silent = torch.nonzero(amplitude > threshold, as_tuple=False).flatten()
    first_non_silent = int(non_silent[0].item())
    last_non_silent = int(non_silent[-1].item())

    leading_silence_samples = first_non_silent
    trailing_silence_samples = int(mono_waveform.numel()) - last_non_silent - 1

    leading_delta = target_samples - leading_silence_samples
    if leading_delta > 0:
        waveform = F.pad(waveform, (leading_delta, 0), "constant", 0.0)
    else:
        trim_from_start = min(-leading_delta, int(waveform.size(-1)))
        waveform = waveform[..., trim_from_start:]

    trailing_delta = target_samples - trailing_silence_samples
    if trailing_delta > 0:
        return F.pad(waveform, (0, trailing_delta), "constant", 0.0)

    trim_from_end = min(-trailing_delta, int(waveform.size(-1)))
    if trim_from_end <= 0:
        return waveform
    return waveform[..., :-trim_from_end]


def compute_num_audio_tokens(
    num_samples: int, *, audio_samples_per_llm_token: int
) -> int:
    if num_samples % audio_samples_per_llm_token != 0:
        raise ValueError(
            f"Waveform length {num_samples} is not aligned to token hop {audio_samples_per_llm_token}."
        )
    return num_samples // audio_samples_per_llm_token