-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdataset.py
More file actions
128 lines (95 loc) · 3.6 KB
/
Copy pathdataset.py
File metadata and controls
128 lines (95 loc) · 3.6 KB
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
from dataclasses import dataclass
import torch
from torch import nn
import torchaudio
import librosa
from typing import Tuple, Dict, Optional, List, Union
from torch.nn.utils.rnn import pad_sequence
@dataclass
class MelSpectrogramConfig:
sr: int = 22050
win_length: int = 1024
hop_length: int = 256
n_fft: int = 1024
f_min: int = 0
f_max: int = 8000
n_mels: int = 80
power: float = 1.0
# value of melspectrograms if we fed a silence into `MelSpectrogram`
pad_value: float = -11.5129251
class MelSpectrogram(nn.Module):
def __init__(self, config: MelSpectrogramConfig):
super(MelSpectrogram, self).__init__()
self.config = config
self.mel_spectrogram = torchaudio.transforms.MelSpectrogram(
sample_rate=config.sr,
win_length=config.win_length,
hop_length=config.hop_length,
n_fft=config.n_fft,
f_min=config.f_min,
f_max=config.f_max,
n_mels=config.n_mels
)
# The is no way to set power in constructor in 0.5.0 version.
self.mel_spectrogram.spectrogram.power = config.power
# Default `torchaudio` mel basis uses HTK formula. In order to be compatible with WaveGlow
# we decided to use Slaney one instead (as well as `librosa` does by default).
mel_basis = librosa.filters.mel(
sr=config.sr,
n_fft=config.n_fft,
n_mels=config.n_mels,
fmin=config.f_min,
fmax=config.f_max
).T
self.mel_spectrogram.mel_scale.fb.copy_(torch.tensor(mel_basis))
def forward(self, audio: torch.Tensor) -> torch.Tensor:
"""
:param audio: Expected shape is [B, T]
:return: Shape is [B, n_mels, T']
"""
mel = self.mel_spectrogram(audio) \
.clamp_(min=1e-5) \
.log_()
return mel
class LJSpeechDataset(torchaudio.datasets.LJSPEECH):
def __init__(self, root):
super().__init__(root=root)
self._tokenizer = torchaudio.pipelines.TACOTRON2_GRIFFINLIM_CHAR_LJSPEECH.get_text_processor()
def __getitem__(self, index: int):
waveform, _, _, transcript = super().__getitem__(index)
waveforn_length = torch.tensor([waveform.shape[-1]]).int()
tokens, token_lengths = self._tokenizer(transcript)
return waveform, waveforn_length, transcript, tokens, token_lengths
def decode(self, tokens, lengths):
result = []
for tokens_, length in zip(tokens, lengths):
text = "".join([
self._tokenizer.tokens[token]
for token in tokens_[:length]
])
result.append(text)
return result
@dataclass
class Batch:
waveform: torch.Tensor
waveforn_length: torch.Tensor
transcript: List[str]
tokens: torch.Tensor
token_lengths: torch.Tensor
durations: Optional[torch.Tensor] = None
def to(self, device: torch.device) -> 'Batch':
raise NotImplementedError
class LJSpeechCollator:
def __call__(self, instances: List[Tuple]) -> Dict:
waveform, waveforn_length, transcript, tokens, token_lengths = list(
zip(*instances)
)
waveform = pad_sequence([
waveform_[0] for waveform_ in waveform
]).transpose(0, 1)
waveforn_length = torch.cat(waveforn_length)
tokens = pad_sequence([
tokens_[0] for tokens_ in tokens
]).transpose(0, 1)
token_lengths = torch.cat(token_lengths)
return Batch(waveform, waveforn_length, transcript, tokens, token_lengths)