fakesound 0.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. fakesound-0.0.0/LICENSE +18 -0
  2. fakesound-0.0.0/PKG-INFO +6 -0
  3. fakesound-0.0.0/README.md +39 -0
  4. fakesound-0.0.0/pyproject.toml +14 -0
  5. fakesound-0.0.0/setup.cfg +4 -0
  6. fakesound-0.0.0/src/fakesound/data/dataset.py +58 -0
  7. fakesound-0.0.0/src/fakesound/data/mel_bands.py +34 -0
  8. fakesound-0.0.0/src/fakesound/data/pqmf.py +21 -0
  9. fakesound-0.0.0/src/fakesound/data/transforms.py +168 -0
  10. fakesound-0.0.0/src/fakesound/gansynth/dataset.py +46 -0
  11. fakesound-0.0.0/src/fakesound/gansynth/models.py +89 -0
  12. fakesound-0.0.0/src/fakesound/gansynth/utils.py +31 -0
  13. fakesound-0.0.0/src/fakesound/hifigan/blocks.py +57 -0
  14. fakesound-0.0.0/src/fakesound/hifigan/loss_funcs.py +29 -0
  15. fakesound-0.0.0/src/fakesound/hifigan/models.py +168 -0
  16. fakesound-0.0.0/src/fakesound/rave/core_funcs.py +46 -0
  17. fakesound-0.0.0/src/fakesound/rave/discriminator.py +47 -0
  18. fakesound-0.0.0/src/fakesound/rave/encoder.py +28 -0
  19. fakesound-0.0.0/src/fakesound/rave/generator.py +102 -0
  20. fakesound-0.0.0/src/fakesound/rave/model.py +31 -0
  21. fakesound-0.0.0/src/fakesound/samplernn/blocks.py +65 -0
  22. fakesound-0.0.0/src/fakesound/samplernn/model.py +75 -0
  23. fakesound-0.0.0/src/fakesound/util/train_utils.py +31 -0
  24. fakesound-0.0.0/src/fakesound/wavenet/blocks.py +18 -0
  25. fakesound-0.0.0/src/fakesound/wavenet/layers.py +41 -0
  26. fakesound-0.0.0/src/fakesound/wavenet/model.py +50 -0
  27. fakesound-0.0.0/src/fakesound.egg-info/PKG-INFO +6 -0
  28. fakesound-0.0.0/src/fakesound.egg-info/SOURCES.txt +28 -0
  29. fakesound-0.0.0/src/fakesound.egg-info/dependency_links.txt +1 -0
  30. fakesound-0.0.0/src/fakesound.egg-info/top_level.txt +1 -0
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 groma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: fakesound
3
+ Version: 0.0.0
4
+ Requires-Python: >=3.10
5
+ License-File: LICENSE
6
+ Dynamic: license-file
@@ -0,0 +1,39 @@
1
+ # fakesound
2
+
3
+ fakesound is a python library for neural audio synthesis.
4
+
5
+ ## Description
6
+
7
+ This library contains re-implementations of several classic neural audio synthesis algorithms. At the moment the following are implemented:
8
+
9
+
10
+ - Wavenet
11
+ - SampleRNN
12
+ - GANSynth
13
+ - HiFi-GAN
14
+ - RAVE v1
15
+
16
+ The library is still highly experimental, many of the models have been barely tested. Use at your own risk!
17
+
18
+ ## How to use
19
+ Check the *examples* folder for some example training and generation scripts. This is all for now!
20
+ ### Dependencies
21
+
22
+ - scipy
23
+ - numpy
24
+ - pytorch
25
+ - tensorboard
26
+ - auraloss
27
+ - python-soundfile
28
+
29
+
30
+ ## Authors
31
+
32
+ Gerard Roma, 2026
33
+
34
+ Some of the implementations are heavily based on the original implementation.
35
+
36
+
37
+ ## License
38
+
39
+ This project is licensed under the MIT License - see the LICENSE.md file for details
@@ -0,0 +1,14 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fakesound"
7
+ version = "0.0.0"
8
+ requires-python = ">=3.10"
9
+
10
+ [tool.setuptools]
11
+ package-dir = {"" = "src"}
12
+
13
+ [tool.setuptools.packages.find]
14
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,58 @@
1
+ from collections.abc import Callable
2
+ import numpy as np
3
+ import glob, os
4
+ import torch
5
+ from scipy.io import wavfile
6
+ from numpy.lib.stride_tricks import sliding_window_view
7
+ from torch.utils.data import Dataset
8
+
9
+ class AudioDataSet(Dataset[torch.Tensor]):
10
+ def __init__(self, folder_path:str, chunk_samples:int,
11
+ hop_samples:int, transform:Callable = None,
12
+ reset = False, pad_start:int = 0):
13
+ self.sample_rate = None # set when 1st loading audio
14
+ self.transform = transform
15
+ files = glob.glob(os.path.join(folder_path, "*.wav"))
16
+ arrays = []
17
+ self.reset_flags = []
18
+ # if reset is used, all files should be equal length
19
+ self.num_chunks = []
20
+ for f in files:
21
+ x = self.load_audio(f)
22
+ x = np.pad(x, (pad_start, 0))
23
+ chunks = sliding_window_view(x, chunk_samples)[::hop_samples,:]
24
+ self.num_chunks.append(len(chunks))
25
+ file_reset = [False] * self.num_chunks[-1]
26
+ if reset:
27
+ file_reset[0] = True
28
+ arrays.append(chunks)
29
+ self.reset_flags = self.reset_flags + file_reset
30
+ self.data = np.concatenate(arrays, 0)
31
+
32
+
33
+ def __len__(self):
34
+ return len(self.data)
35
+
36
+ # reset assumes no shuffling
37
+ def __getitem__(self, index:int):
38
+ sample = self.data[index]
39
+ reset = self.reset_flags[index]
40
+ sample = torch.from_numpy(sample).unsqueeze(0).float()
41
+ transformed = sample
42
+ if self.transform is not None:
43
+ transformed = self.transform(sample)
44
+ return transformed, sample, reset
45
+
46
+ def load_audio(self, file_path:str):
47
+ samplerate, data = wavfile.read(file_path)
48
+ if self.sample_rate is None:
49
+ self.sample_rate = samplerate
50
+ if samplerate != self.sample_rate:
51
+ raise Exception("Different sample rates in dataset")
52
+ if data.dtype == np.int16:
53
+ data = data / 2**15
54
+ if data.dtype == np.int32:
55
+ data = data / 2**31
56
+ if len(data.shape) > 1:
57
+ data = data[:,0]
58
+ return data
@@ -0,0 +1,34 @@
1
+ import numpy as np
2
+ import torch
3
+
4
+ def mel2hz(mel):
5
+ return 700. * (np.exp(mel / 1127.) - 1.0)
6
+
7
+ def hz2mel(hz):
8
+ return 1127. * np.log(1.0 + (hz / 700.0))
9
+
10
+ def linear2mel(bin_freqs:np.ndarray, n_mels:int, fmin:float, fmax:float):
11
+ return get_mel_matrix(bin_freqs, n_mels, fmin, fmax)
12
+
13
+ def mel2linear(bin_freqs:np.ndarray, n_mels:int, fmin:float, fmax:float):
14
+ M = get_mel_matrix(bin_freqs, n_mels, fmin, fmax)
15
+ Mt = M.T
16
+ S = np.sum(np.dot(M, Mt), 0)
17
+ d = np.divide(1.0, S, out=np.zeros_like(S), where = S!=0)
18
+ return np.dot(Mt, np.diag(d))
19
+
20
+ def get_mel_matrix(bin_freqs:np.ndarray, n_mels:int, fmin:float, fmax:float):
21
+ # based on https://librosa.org/doc/main/_modules/librosa/filters.html#mel
22
+ N = bin_freqs.shape[0]
23
+ M = np.zeros((n_mels,N))
24
+ mels = np.linspace(hz2mel(fmin), hz2mel(fmax), n_mels+2)
25
+ mel_freqs = mel2hz(mels)
26
+ fdiff = np.diff(mel_freqs)
27
+ ramps = np.subtract.outer(mel_freqs, bin_freqs)
28
+ for i in range(n_mels):
29
+ rising = -ramps[i, :] / fdiff[i]
30
+ falling = ramps[i + 2, :] / fdiff[i + 1]
31
+ M[i, :] = np.maximum(0, np.minimum(rising, falling))
32
+ norm = 2.0 / (mel_freqs[2 : n_mels + 2] - mel_freqs[:n_mels])
33
+ M = M * norm[:, None]
34
+ return M
@@ -0,0 +1,21 @@
1
+ import numpy as np
2
+ import torch
3
+ import torch.nn.functional as F
4
+ from scipy import signal
5
+
6
+ def get_pqmf_filter( n_bands:int = 16, atten:float=100, analysis = True):
7
+ cutoff = 1 / (2 * n_bands)
8
+ n_taps, beta = signal.kaiserord(atten, cutoff)
9
+ h = signal.firwin(n_taps + 1, cutoff,
10
+ window=('kaiser', beta), scale = False )
11
+ k = torch.arange(n_bands).reshape(-1, 1)
12
+ alpha = (2 * k + 1) * (torch.pi /
13
+ (2 * n_bands)) * (torch.arange( n_taps + 1)-(( n_taps - 1) / 2))
14
+ phase = (-1)**k * np.pi / 4
15
+ if analysis:
16
+ f = 2 * torch.from_numpy(h) * torch.cos(alpha + phase)
17
+ f = f[:, None, :].float()
18
+ else:
19
+ f = 2 * torch.from_numpy(h) * torch.cos(alpha - phase)
20
+ f = f[None, :, :].float()
21
+ return f
@@ -0,0 +1,168 @@
1
+ import numpy as np
2
+ import torch
3
+ from torch import nn
4
+ import torch.nn.functional as F
5
+ from .mel_bands import linear2mel
6
+ from .pqmf import get_pqmf_filter
7
+
8
+ class muLawEncoder(nn.Module):
9
+ def __init__(self, n_levels:int = 256):
10
+ super().__init__()
11
+ self.num_levels:int = n_levels
12
+ bins:torch.Tensor = torch.linspace(-1, 1, n_levels)
13
+ mu:torch.Tensor = torch.Tensor([n_levels - 1])
14
+ self.register_buffer('bins', bins)
15
+ self.register_buffer('mu', mu)
16
+
17
+ def forward(self, x:torch.Tensor) -> torch.Tensor:
18
+ x = x.clone()
19
+ compressed = torch.sign(x) * torch.log1p(self.mu * torch.abs(x)) / torch.log1p(self.mu)
20
+ return torch.bucketize(compressed, self.bins)
21
+
22
+ class muLawDecoder(nn.Module):
23
+ def __init__(self, n_levels:int = 256):
24
+ super().__init__()
25
+ mu:torch.Tensor = torch.Tensor([n_levels - 1])
26
+ self.register_buffer('mu', mu)
27
+
28
+ def forward(self, x:torch.Tensor) -> torch.Tensor:
29
+ x = x / (1 + self.mu) * 2 - 1
30
+ return torch.sign(x) * (torch.pow(self.mu + 1, torch.abs(x)) - 1) / self.mu
31
+
32
+ class Wave2LogMagIF(nn.Module):
33
+ def __init__(self, fft_size:int, hop_size:int):
34
+ super().__init__()
35
+ self.fft_size = fft_size
36
+ self.hop_size = hop_size
37
+ self.epsilon = 1e-7
38
+ window = torch.hann_window(fft_size)
39
+ self.register_buffer('window', window)
40
+
41
+ def forward(self, x:torch.Tensor, remove_nyquist:bool = True) -> tuple[torch.Tensor, torch.Tensor]:
42
+ x = x.squeeze(1)
43
+ spec = torch.stft(x,
44
+ n_fft = self.fft_size,
45
+ hop_length = self.hop_size,
46
+ win_length = self.fft_size,
47
+ window = self.window,
48
+ center = True,
49
+ pad_mode = 'reflect',
50
+ normalized = False,
51
+ onesided = True,
52
+ return_complex = True).squeeze(0)
53
+ logmag = torch.log(torch.abs(spec).clip(min = self.epsilon))
54
+ phase = torch.angle(spec)
55
+ dphase = torch.diff(phase)
56
+ dphase = torch.where(dphase > np.pi, dphase - 2 * np.pi, dphase)
57
+ dphase = torch.where(dphase < -np.pi, dphase + 2 * np.pi, dphase)
58
+ dphase = torch.hstack((phase[:,0, None], dphase))
59
+ if remove_nyquist:
60
+ return (logmag[:-1,:], dphase[:-1,:])
61
+ else:
62
+ return (logmag, dphase)
63
+
64
+ class LogMagIF2Wave(nn.Module):
65
+ def __init__(self, fft_size:int, hop_size:int):
66
+ super().__init__()
67
+ self.fft_size = fft_size
68
+ self.hop_size = hop_size
69
+ window = torch.hann_window(fft_size)
70
+ self.register_buffer('window', window)
71
+
72
+ def forward(self, logmag:torch.Tensor, instfreq: torch.Tensor, add_nyquist:bool = True) -> torch.Tensor:
73
+ mag = torch.exp(logmag)
74
+ phase = torch.cumsum(instfreq, 1)
75
+ spec = torch.polar(mag, phase)
76
+ if add_nyquist:
77
+ new_spec = torch.empty(
78
+ logmag.shape[0]+1, logmag.shape[1] ,
79
+ dtype = spec.dtype).to(logmag.device)
80
+ new_spec[:logmag.shape[0], :] = spec
81
+ spec = new_spec
82
+ audio = torch.istft(spec,
83
+ n_fft = self.fft_size,
84
+ hop_length = self.hop_size,
85
+ win_length = self.fft_size,
86
+ window = self.window,
87
+ center = True,
88
+ normalized = False,
89
+ onesided = True,
90
+ return_complex = False
91
+ )
92
+ return audio
93
+
94
+
95
+ class Wave2LogMelIF(nn.Module):
96
+ def __init__(self, sr:float, fft_size:int, hop_size:int,
97
+ n_mels:int, fmin:float, fmax:float,
98
+ pad_amount:int = None, return_phase:bool = True):
99
+ super().__init__()
100
+ self.fft_size = fft_size
101
+ self.hop_size = hop_size
102
+ self.n_mels = n_mels
103
+ self.return_phase = return_phase
104
+ self.epsilon = 1e-7
105
+ if pad_amount is None:
106
+ self.pad_amount = fft_size //2
107
+ else:
108
+ self.pad_amount = pad_amount
109
+ dF = sr/fft_size
110
+ n_bins = fft_size / 2 + 1
111
+ ff = np.arange(0, dF * n_bins, dF)
112
+ mel_matrix = torch.from_numpy(linear2mel(ff, n_mels, fmin, fmax)).float()
113
+ window = torch.hann_window(fft_size)
114
+ self.register_buffer('mel_matrix', mel_matrix)
115
+ self.register_buffer('window', window)
116
+
117
+ def forward(self, x:torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
118
+ x = torch.nn.functional.pad(
119
+ x.unsqueeze(1), (self.pad_amount, self.pad_amount),
120
+ mode='reflect'
121
+ )
122
+ x = x.squeeze(1)
123
+ spec = torch.stft(x,
124
+ n_fft = self.fft_size,
125
+ hop_length = self.hop_size,
126
+ win_length = self.fft_size,
127
+ window = self.window,
128
+ center= False,
129
+ pad_mode = 'reflect',
130
+ normalized = False,
131
+ onesided = True,
132
+ return_complex = True).squeeze(0)
133
+ mel_mag = torch.matmul(self.mel_matrix, torch.abs(spec))
134
+ mel_logmag = torch.log(mel_mag.clip(min = self.epsilon))
135
+ if not self.return_phase: return (mel_logmag, torch.empty(0))
136
+ mel_phase = torch.matmul(self.mel_matrix, torch.angle(spec))
137
+ dphase = torch.diff(mel_phase)
138
+ dphase = torch.where(dphase > np.pi, dphase - 2 * np.pi, dphase)
139
+ dphase = torch.where(dphase < -np.pi, dphase + 2 * np.pi, dphase)
140
+ dphase = torch.hstack((mel_phase[:,0, None], dphase))
141
+ return (mel_logmag, dphase)
142
+
143
+ class Wave2PQMF(nn.Module):
144
+ def __init__(self, n_bands:int = 16, atten:float = 100):
145
+ super().__init__()
146
+ h = get_pqmf_filter(n_bands, atten)
147
+ self.n_taps = h.shape[-1]-1
148
+ self.n_bands = n_bands
149
+ self.register_buffer("h", h)
150
+ def forward(self, x:torch.Tensor)-> torch.Tensor:
151
+ return F.conv1d(x, self.h, padding = self.n_taps // 2, stride=self.n_bands)
152
+
153
+ class PQMF2Wave(nn.Module):
154
+ def __init__(self, n_bands:int = 16, atten:float = 100):
155
+ super().__init__()
156
+ h = get_pqmf_filter(n_bands, atten, False)
157
+ self.n_taps = h.shape[-1]-1
158
+ self.n_bands = n_bands
159
+ self.register_buffer("h", h)
160
+ updown_filter = torch.zeros((n_bands, n_bands, n_bands)).float()
161
+ updown_filter[range(n_bands), range(n_bands), 0] = 1
162
+ self.register_buffer("updown_filter", updown_filter)
163
+ self.pad_fn = torch.nn.ConstantPad1d(self.n_taps // 2, 0.0)
164
+
165
+ def forward(self, x:torch.Tensor)-> torch.Tensor:
166
+ x = F.conv_transpose1d(x, self.updown_filter * self.n_bands,stride=self.n_bands)
167
+ x = F.conv1d(x, self.h, padding = self.n_taps // 2)
168
+ return x
@@ -0,0 +1,46 @@
1
+ import numpy as np
2
+ import glob, os
3
+ import torch
4
+ from fakesound.data.dataset import AudioDataSet
5
+ from fakesound.data.transforms import Wave2LogMagIF
6
+
7
+ # Lazy dataset for NSynth training set including pitch
8
+
9
+ class NSynthDataset(AudioDataSet):
10
+ def __init__(self, folder_path:str, sample_rate:int,
11
+ fft_size:int, hop_size:int, pad_start:int):
12
+ self.sample_rate = None
13
+ self.fft_size = fft_size
14
+ self.n_mels = fft_size // 2
15
+ self.pad_start = pad_start
16
+ self.transform = Wave2LogMagIF(fft_size, hop_size)
17
+
18
+ self.files = glob.glob(os.path.join(folder_path, "*.wav"))[:16]
19
+
20
+ self.max_mag = np.log(fft_size//4)
21
+ self.min_mag = np.log(self.transform.epsilon)
22
+ self.max_phase = np.pi
23
+ self.min_phase = -np.pi
24
+ def __len__(self):
25
+ return len(self.files)
26
+
27
+ def normalise_mag(self, x:torch.Tensor)-> torch.Tensor:
28
+ return 2 * (x - self.min_mag) / (self.max_mag - self.min_mag) -1
29
+ def normalise_phase(self, x:torch.Tensor)-> torch.Tensor:
30
+ return 2 * (x - self.min_phase) / (self.max_phase - self.min_phase) -1
31
+ def denormalise_mag(self, x:torch.Tensor)-> torch.Tensor:
32
+ return (x + 1)* (self.max_mag - self.min_mag) / 2 + self.min_mag
33
+ def denormalise_phase(self, x:torch.Tensor)-> torch.Tensor:
34
+ return (x + 1)* (self.max_phase - self.min_phase) / 2 + self.min_phase
35
+
36
+ # reset assumes no shuffling
37
+ def __getitem__(self, index:int):
38
+ filename = self.files[index]
39
+ pitch = int(filename.split("_")[-1].split("-")[1])
40
+ sample = self.load_audio(filename)
41
+ sample = np.pad(sample, (self.pad_start, 0))
42
+ sample = torch.from_numpy(sample).unsqueeze(0).float()
43
+ logmag, phase = self.transform(sample)
44
+ logmag = self.normalise_mag(logmag)
45
+ phase = self.normalise_phase(phase)
46
+ return torch.stack((logmag, phase)), pitch
@@ -0,0 +1,89 @@
1
+ import torch
2
+ from torch import nn
3
+ from torch.nn import functional as F
4
+
5
+ class PixelNorm(nn.Module):
6
+ def forward(self, x):
7
+ n_chan = x.shape[1]
8
+ return x / (1/ n_chan * (x ** 2).sum(dim = 1, keepdims = True)).sqrt()
9
+
10
+ class Generator(nn.Module):
11
+ def __init__(self, input_size:int = 317):
12
+ super().__init__()
13
+ self.input_size = input_size
14
+ self.input_block = nn.Sequential(
15
+ nn.Conv2d(input_size, 256, (16, 2)),
16
+ PixelNorm(), nn.LeakyReLU(),
17
+ nn.Conv2d(256, 256, 3, padding="same"),
18
+ PixelNorm(), nn.LeakyReLU())
19
+ self.upsample2x2 = lambda c: nn.ConvTranspose2d(c, c, (2,2),(2,2))
20
+ self.conv_block = lambda in_c, out_c: nn.Sequential(
21
+ nn.Conv2d(in_c, out_c, 3, padding="same"),
22
+ PixelNorm(), nn.LeakyReLU(),
23
+ nn.Conv2d(out_c, out_c, 3, padding="same"),
24
+ PixelNorm(), nn.LeakyReLU())
25
+ self.output = nn.Sequential(
26
+ nn.Conv2d(32, 2, 1),
27
+ nn.Tanh()
28
+ )
29
+
30
+ self.network = nn.Sequential(
31
+ self.input_block,
32
+ self.upsample2x2(256),
33
+ self.conv_block(256, 256),
34
+ self.upsample2x2(256),
35
+ self.conv_block(256, 256),
36
+ self.upsample2x2(256),
37
+ self.conv_block(256, 256),
38
+ self.upsample2x2(256),
39
+ self.conv_block(256, 128),
40
+ self.upsample2x2(128),
41
+ self.conv_block(128, 64),
42
+ self.upsample2x2(64),
43
+ self.conv_block(64, 32),
44
+ self.output
45
+ )
46
+ def forward(self, x:torch.Tensor)-> torch.Tensor:
47
+ x = F.pad(x,(1, 1, 15, 15))
48
+ return self.network(x)
49
+
50
+ class Discriminator(nn.Module):
51
+ def __init__(self, num_classes:int):
52
+ super().__init__()
53
+ self.input = nn.Conv2d(2, 32, 1)
54
+ self.downsample2x2 = lambda c: nn.Conv2d(c, c, (2,2),(2,2))
55
+ self.conv_block = lambda in_c, out_c: nn.Sequential(
56
+ nn.Conv2d(in_c, out_c, 3, padding = "same"),
57
+ nn.LeakyReLU(),
58
+ nn.Conv2d(out_c, out_c, 3, padding = "same"),
59
+ nn.LeakyReLU()
60
+ )
61
+ self.last_conv = nn.Sequential(
62
+ nn.Conv2d(256, 256, 3, padding="same"),
63
+ nn.LeakyReLU(),
64
+ nn.Conv2d(256, 256, (16,2), padding="valid"),
65
+ nn.LeakyReLU()
66
+ )
67
+ self.disc_out = nn.Linear(256, 1)
68
+ self.classifier_out = nn.Linear(256, num_classes)
69
+
70
+ self.network = nn.Sequential(
71
+ self.input,
72
+ self.conv_block(32, 32),
73
+ self.downsample2x2(32),
74
+ self.conv_block(32, 64),
75
+ self.downsample2x2(64),
76
+ self.conv_block(64, 128),
77
+ self.downsample2x2(128),
78
+ self.conv_block(128, 256),
79
+ self.downsample2x2(256),
80
+ self.conv_block(256, 256),
81
+ self.downsample2x2(256),
82
+ self.conv_block(256, 256),
83
+ self.downsample2x2(256),
84
+ self.conv_block(256, 256),
85
+ self.last_conv
86
+ )
87
+ def forward(self, x:torch.Tensor)-> torch.Tensor:
88
+ x = self.network(x).flatten(1)
89
+ return self.disc_out(x), self.classifier_out(x)
@@ -0,0 +1,31 @@
1
+ import torch
2
+ from torch import nn
3
+ import torch.nn.functional as F
4
+
5
+ gp_beta = 10
6
+ pitch_beta = 10
7
+ crossentropy = nn.CrossEntropyLoss()
8
+
9
+ # https://github.com/EmilienDupont/wgan-gp/
10
+ def gradient_penalty(real_data, fake_data, batch_size, disc, device):
11
+ alpha = torch.rand(batch_size, 1, 1, 1)
12
+ alpha = alpha.expand_as(real_data).to(device)
13
+ interpolated = alpha * real_data.detach() + (1 - alpha) * fake_data.detach()
14
+ interpolated = torch.autograd.Variable(interpolated, requires_grad=True)
15
+ prob, _ = disc(interpolated)
16
+ gradients = torch.autograd.grad(outputs = prob,
17
+ inputs = interpolated,
18
+ grad_outputs = torch.ones_like(prob),
19
+ create_graph = True, retain_graph = True)[0]
20
+
21
+ gradients = gradients.view(batch_size, -1)
22
+ gradient_penalty = ((gradients.norm(2, dim=1) - 1) ** 2).mean()
23
+ return gradient_penalty
24
+
25
+ def create_fake_data(generator, config):
26
+ fake_pitch = torch.randint( 0, 61, (config.batch_size,)).unsqueeze(1).to(config.device)
27
+ fake_pitch_vector = torch.zeros(config.batch_size, 61).to(config.device).scatter_(1, fake_pitch, 1).unsqueeze(2).unsqueeze(3).to(config.device)
28
+ noise = torch.randn(config.batch_size, config.latent_size, 1, 1).to(config.device)
29
+ gen_input = torch.cat((noise, fake_pitch_vector), 1)
30
+ fake_mel = generator(gen_input)
31
+ return fake_pitch, fake_mel
@@ -0,0 +1,57 @@
1
+ # based on https://github.com/jik876/hifi-gan
2
+ # only v1 implemented
3
+
4
+ import torch
5
+ from torch import nn
6
+ from torch.nn.utils import remove_weight_norm
7
+ from torch.nn.utils.parametrizations import weight_norm
8
+
9
+
10
+ class ResidualBlock(torch.nn.Module):
11
+ def __init__(self, channels:int, kernel_size:int = 3, dilations:list[int] = [1, 3, 5]):
12
+ super().__init__()
13
+ self.conv = lambda d: weight_norm(
14
+ nn.Conv1d(channels, channels, kernel_size, 1,
15
+ dilation = d, padding = (kernel_size * d - d) // 2)
16
+ )
17
+ self.convs = nn.Sequential()
18
+ for d in dilations:
19
+ self.convs.extend(nn.Sequential(*[
20
+ nn.LeakyReLU(0.1), self.conv(d), nn.LeakyReLU(0.1), self.conv(1)
21
+ ]))
22
+ def forward(self, x:torch.Tensor)->torch.Tensor:
23
+ return x + self.convs(x)
24
+
25
+ def remove_weight_norm(self):
26
+ for l in self.convs:
27
+ if type(l) == nn.Conv1d:
28
+ remove_weight_norm(l)
29
+
30
+
31
+ class UpsamplingBlock(torch.nn.Module):
32
+ def __init__(self, in_channels:int, out_channels:int, kernel_size:int, stride:int,
33
+ res_channels:int, res_kernels:list[int], res_dilations:list[int]):
34
+ assert(len(res_kernels) == len(res_dilations))
35
+ super().__init__()
36
+ self.num_res_blocks = len(res_kernels)
37
+ self.relu = nn.LeakyReLU(0.1)
38
+ self.conv_transpose = nn.ConvTranspose1d(
39
+ in_channels,out_channels, kernel_size, stride,
40
+ (kernel_size - stride ) // 2)
41
+ self.res_blocks = nn.ModuleList(
42
+ [ResidualBlock(res_channels, res_kernels[i], res_dilations)
43
+ for i in range(self.num_res_blocks)]
44
+ )
45
+
46
+ def forward(self, x:torch.Tensor)->torch.Tensor:
47
+ x = self.conv_transpose(self.relu(x))
48
+ xs = 0
49
+ for res_block in self.res_blocks:
50
+ xs += res_block(x)
51
+ x = xs / self.num_res_blocks
52
+ return x
53
+
54
+ def remove_weight_norm(self):
55
+ remove_weight_norm(self.conv_transpose)
56
+ for res_block in self.res_blocks:
57
+ res_block.remove_weight_norm()
@@ -0,0 +1,29 @@
1
+ import torch
2
+
3
+ def feature_loss(fmap_real:list, fmap_fake:list):
4
+ loss = 0
5
+ for disc_real, disc_fake in zip(fmap_real, fmap_fake):
6
+ for layer_real, layer_fake in zip(disc_real, disc_fake):
7
+ loss += torch.mean(torch.abs(layer_real - layer_fake))
8
+ return loss * 2
9
+
10
+ def discriminator_loss(disc_real:list, disc_fake:list):
11
+ loss = 0
12
+ real_losses = []
13
+ fake_losses = []
14
+ for subdisc_real, subdisc_fake in zip(disc_real, disc_fake):
15
+ real_loss = torch.mean((1 - subdisc_real) ** 2)
16
+ fake_loss = torch.mean(subdisc_fake ** 2)
17
+ loss += (real_loss + fake_loss)
18
+ real_losses.append(real_loss.item())
19
+ fake_losses.append(fake_loss.item())
20
+ return loss, real_losses, fake_losses
21
+
22
+ def generator_loss(disc_outputs:list):
23
+ loss = 0
24
+ generator_losses = []
25
+ for subdisc_fake in disc_outputs:
26
+ l = torch.mean((1 - subdisc_fake) ** 2)
27
+ generator_losses.append(l)
28
+ loss += l
29
+ return loss, generator_losses