Vansh Chugh commited on
Commit
fb49998
·
1 Parent(s): 08fde90

initial deploy

Browse files
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .DS_Store
4
+ banquet-repo/
README.md CHANGED
@@ -1,15 +1,24 @@
1
  ---
2
  title: Banquet
3
- emoji: 📉
4
  colorFrom: purple
5
  colorTo: green
6
  sdk: gradio
7
- sdk_version: 6.20.0
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
11
  license: mit
12
- short_description: todo
13
  ---
14
 
15
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Banquet
3
+ emoji: 🍽️
4
  colorFrom: purple
5
  colorTo: green
6
  sdk: gradio
7
+ sdk_version: 5.28.0
8
+ python_version: '3.11'
9
  app_file: app.py
10
  pinned: false
11
  license: mit
12
+ short_description: Separate any instrument from a mix using an audio query
13
  ---
14
 
15
+ # Banquet
16
+
17
+ Query-based music source separation: give it a mixture and a 10-second audio
18
+ example of the instrument you want, and it extracts that instrument from the
19
+ mix. No fixed vocals/drums/bass/other setup — any instrument you can supply
20
+ an example of, including niche ones like reeds or organ.
21
+
22
+ Paper: [A Stem-Agnostic Single-Decoder System for Music Source Separation
23
+ Beyond Four Stems](https://arxiv.org/abs/2406.18747) (Watcharasupat & Lerch,
24
+ ISMIR 2024).
app.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+
3
+ sys.stdout.reconfigure(line_buffering=True)
4
+
5
+ try:
6
+ import spaces
7
+ except ImportError:
8
+ # keep @spaces.GPU usable as a no-op; ZeroGPU requires this exact name.
9
+ class spaces:
10
+ class GPU:
11
+ def __init__(self, func=None, duration=60):
12
+ self.func = func
13
+
14
+ def __call__(self, *args, **kwargs):
15
+ if self.func is not None:
16
+ return self.func(*args, **kwargs)
17
+ func = args[0]
18
+ return func
19
+
20
+ import os
21
+ import threading
22
+ from types import SimpleNamespace
23
+
24
+ import torch
25
+ import torchaudio
26
+ import gradio as gr
27
+ from audiotools import AudioSignal
28
+ from pyharp import ModelCard, build_endpoint, load_audio, save_audio
29
+
30
+ from core.models.ebase import EndToEndLightningSystem
31
+ from core.models.e2e.bandit.bandit import PasstFiLMConditionedBandit
32
+
33
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
34
+
35
+ CKPT_PATH = os.path.join(os.path.dirname(__file__), "ev-pre-aug.ckpt")
36
+ MODEL_FS = 44100
37
+ QUERY_LENGTH_SECONDS = 10.0
38
+
39
+ # inference chunking (default: chunk_size_seconds=6.0, hop_size_seconds=0.5,
40
+ # batch_size=12, per repo config config/data/moisesdb-test.yml) — internal
41
+ # windowing detail, not something a musician can meaningfully tune.
42
+ CHUNK_SIZE_SECONDS = 6.0
43
+ HOP_SIZE_SECONDS = 0.5
44
+ INFERENCE_BATCH_SIZE = 12
45
+
46
+ # architecture kwargs, per repo config config/models/bandit-query-pre.yml
47
+ MODEL_KWARGS = dict(
48
+ in_channel=2,
49
+ band_type="musical",
50
+ n_bands=64,
51
+ additive_film=True,
52
+ multiplicative_film=True,
53
+ film_depth=2,
54
+ n_sqm_modules=8,
55
+ emb_dim=128,
56
+ rnn_dim=256,
57
+ bidirectional=True,
58
+ rnn_type="GRU",
59
+ mlp_dim=512,
60
+ hidden_activation="Tanh",
61
+ hidden_activation_kwargs=None,
62
+ complex_mask=True,
63
+ use_freq_weights=True,
64
+ n_fft=2048,
65
+ win_length=2048,
66
+ hop_length=512,
67
+ window_fn="hann_window",
68
+ wkwargs=None,
69
+ power=None,
70
+ center=True,
71
+ normalized=True,
72
+ pad_mode="reflect",
73
+ onesided=True,
74
+ fs=MODEL_FS,
75
+ # repo config points this at a stale training-cluster path used only to
76
+ # seed weights before training; our checkpoint below is loaded strict=True
77
+ # right after construction and overwrites all of these anyway.
78
+ pretrain_encoder=None,
79
+ freeze_encoder=False,
80
+ )
81
+
82
+ system = None
83
+ model_ready = False # has the model been moved onto the GPU yet?
84
+ model_loading = True
85
+ model_error = None
86
+
87
+
88
+ def load_model():
89
+ """Build the model and load the checkpoint on CPU only. Do NOT call .to(DEVICE)
90
+ or otherwise touch CUDA here — ZeroGPU only intercepts CUDA calls made inside an
91
+ @spaces.GPU-decorated call, not from a background thread. This split is a no-op
92
+ on CPU-only hardware (DEVICE == "cpu"), so keep it even while testing on a
93
+ personal CPU-tier Space, before GPU code is added later."""
94
+ global system, model_loading, model_error
95
+ try:
96
+ model = PasstFiLMConditionedBandit(**MODEL_KWARGS)
97
+ system = EndToEndLightningSystem.load_from_checkpoint(
98
+ CKPT_PATH,
99
+ map_location="cpu",
100
+ strict=True,
101
+ model=model,
102
+ loss_handler=None,
103
+ metrics=None,
104
+ augmentation_handler=None,
105
+ inference_handler=SimpleNamespace(
106
+ fs=MODEL_FS,
107
+ chunk_size_seconds=CHUNK_SIZE_SECONDS,
108
+ hop_size_seconds=HOP_SIZE_SECONDS,
109
+ batch_size=INFERENCE_BATCH_SIZE,
110
+ ),
111
+ optimization_bundle=None,
112
+ )
113
+ system.eval()
114
+ print("Model loaded (CPU).")
115
+ except Exception as e:
116
+ model_error = str(e)
117
+ print(f"Load error: {e}")
118
+ finally:
119
+ model_loading = False
120
+
121
+
122
+ threading.Thread(target=load_model, daemon=True).start()
123
+
124
+
125
+ model_card = ModelCard(
126
+ name="Banquet",
127
+ description=(
128
+ "Extracts any instrument from a music mixture using a short audio "
129
+ "example as a query, instead of a fixed vocals/drums/bass/other setup."
130
+ ),
131
+ author="Karn N. Watcharasupat and Alexander Lerch",
132
+ tags=["source separation", "music"],
133
+ )
134
+
135
+
136
+ def _load_resampled(path: str) -> torch.Tensor:
137
+ """Loads an audio file and resamples it to the model's sample rate.
138
+ Returns a (channels, samples) float32 tensor."""
139
+ signal = load_audio(path)
140
+ audio = signal.audio_data.squeeze(0)
141
+ if signal.sample_rate != MODEL_FS:
142
+ audio = torchaudio.functional.resample(
143
+ audio, orig_freq=signal.sample_rate, new_freq=MODEL_FS
144
+ )
145
+ return audio
146
+
147
+
148
+ def _ensure_stereo(audio: torch.Tensor) -> torch.Tensor:
149
+ """The model's architecture is built for a fixed 2-channel input; duplicate
150
+ mono uploads to stereo and drop any channels beyond the first two."""
151
+ if audio.shape[0] == 1:
152
+ audio = audio.repeat(2, 1)
153
+ elif audio.shape[0] > 2:
154
+ audio = audio[:2]
155
+ return audio
156
+
157
+
158
+ def _fit_query_length(query: torch.Tensor) -> torch.Tensor:
159
+ """Truncates or tiles the query to exactly 10 seconds."""
160
+ target_len = int(QUERY_LENGTH_SECONDS * MODEL_FS)
161
+ if query.shape[-1] > target_len:
162
+ query = query[:, :target_len]
163
+ elif query.shape[-1] < target_len:
164
+ reps = target_len // query.shape[-1] + 1
165
+ query = query.repeat(1, reps)[:, :target_len]
166
+ return query
167
+
168
+
169
+ @spaces.GPU
170
+ @torch.inference_mode()
171
+ def process_fn(mixture_path: str, query_path: str) -> str:
172
+ """Separates the instrument described by the query clip out of the mixture."""
173
+ global model_ready
174
+
175
+ if model_loading:
176
+ raise gr.Error("Model is still loading, please wait a moment and try again.")
177
+ if system is None:
178
+ raise gr.Error(f"Model failed to load: {model_error}")
179
+ if not model_ready:
180
+ system.to(DEVICE) # only safe here, inside @spaces.GPU
181
+ model_ready = True
182
+
183
+ orig_fs = load_audio(mixture_path).sample_rate
184
+
185
+ mixture = _ensure_stereo(_load_resampled(mixture_path)).unsqueeze(0).to(DEVICE)
186
+ query = _fit_query_length(_load_resampled(query_path)).unsqueeze(0).to(DEVICE)
187
+
188
+ batch = {
189
+ "mixture": {"audio": mixture},
190
+ "query": {"audio": query},
191
+ "metadata": {"stem": ["target"]},
192
+ "estimates": {},
193
+ }
194
+
195
+ out = system.chunked_inference(batch)
196
+ estimate = out["estimates"]["target"]["audio"].squeeze(0).cpu()
197
+
198
+ if orig_fs != MODEL_FS:
199
+ estimate = torchaudio.functional.resample(
200
+ estimate, orig_freq=MODEL_FS, new_freq=orig_fs
201
+ )
202
+
203
+ output_signal = AudioSignal(estimate, sample_rate=orig_fs)
204
+ return save_audio(output_signal)
205
+
206
+
207
+ with gr.Blocks() as demo:
208
+ input_components = [
209
+ gr.Audio(type="filepath", label="Mixture").harp_required(True),
210
+ gr.Audio(
211
+ type="filepath",
212
+ label="Query Example (~10s clip of the instrument you want extracted)",
213
+ ).harp_required(True),
214
+ ]
215
+ output_components = [
216
+ gr.Audio(type="filepath", label="Separated Audio").set_info(
217
+ "The instrument extracted from the mixture, matched to the query example."
218
+ ),
219
+ ]
220
+
221
+ build_endpoint(
222
+ model_card=model_card,
223
+ input_components=input_components,
224
+ output_components=output_components,
225
+ process_fn=process_fn,
226
+ )
227
+
228
+ demo.queue().launch(pwa=True)
core/__init__.py ADDED
File without changes
core/models/__init__.py ADDED
File without changes
core/models/e2e/__init__.py ADDED
File without changes
core/models/e2e/bandit/__init__.py ADDED
File without changes
core/models/e2e/bandit/bandit.py ADDED
@@ -0,0 +1,619 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, List, Optional, Tuple
2
+ from core.models.e2e.bandit.bandsplit import BandSplitModule
3
+ from core.models.e2e.bandit.maskestim import OverlappingMaskEstimationModule
4
+ from core.models.e2e.bandit.tfmodel import SeqBandModellingModule
5
+ from core.models.e2e.bandit.utils import MusicalBandsplitSpecification
6
+ from core.models.e2e.querier.passt import Passt, PasstWrapper
7
+ from core.types import InputType, OperationMode, SimpleishNamespace
8
+ from torch import Tensor, nn
9
+ import torch
10
+
11
+ from core.models.e2e.base import BaseEndToEndModule
12
+ from core.models.e2e.conditioners.film import FiLM
13
+
14
+ import torchaudio as ta
15
+
16
+
17
+ class BaseBandit(BaseEndToEndModule):
18
+
19
+ def __init__(
20
+ self,
21
+ in_channel: int,
22
+ band_type: str = "musical",
23
+ n_bands: int = 64,
24
+ require_no_overlap: bool = False,
25
+ require_no_gap: bool = True,
26
+ normalize_channel_independently: bool = False,
27
+ treat_channel_as_feature: bool = True,
28
+ n_sqm_modules: int = 12,
29
+ emb_dim: int = 128,
30
+ rnn_dim: int = 256,
31
+ bidirectional: bool = True,
32
+ rnn_type: str = "LSTM",
33
+ n_fft: int = 2048,
34
+ win_length: Optional[int] = 2048,
35
+ hop_length: int = 512,
36
+ window_fn: str = "hann_window",
37
+ wkwargs: Optional[Dict] = None,
38
+ power: Optional[int] = None,
39
+ center: bool = True,
40
+ normalized: bool = True,
41
+ pad_mode: str = "constant",
42
+ onesided: bool = True,
43
+ fs: int = 44100,
44
+ ):
45
+ super().__init__()
46
+
47
+ self.instantitate_spectral(
48
+ n_fft=n_fft,
49
+ win_length=win_length,
50
+ hop_length=hop_length,
51
+ window_fn=window_fn,
52
+ wkwargs=wkwargs,
53
+ power=power,
54
+ normalized=normalized,
55
+ center=center,
56
+ pad_mode=pad_mode,
57
+ onesided=onesided,
58
+ )
59
+
60
+ self.instantiate_bandsplit(
61
+ in_channel=in_channel,
62
+ band_type=band_type,
63
+ n_bands=n_bands,
64
+ require_no_overlap=require_no_overlap,
65
+ require_no_gap=require_no_gap,
66
+ normalize_channel_independently=normalize_channel_independently,
67
+ treat_channel_as_feature=treat_channel_as_feature,
68
+ emb_dim=emb_dim,
69
+ n_fft=n_fft,
70
+ fs=fs,
71
+ )
72
+
73
+ self.instantiate_tf_modelling(
74
+ n_sqm_modules=n_sqm_modules,
75
+ emb_dim=emb_dim,
76
+ rnn_dim=rnn_dim,
77
+ bidirectional=bidirectional,
78
+ rnn_type=rnn_type,
79
+ )
80
+
81
+ def instantitate_spectral(
82
+ self,
83
+ n_fft: int = 2048,
84
+ win_length: Optional[int] = 2048,
85
+ hop_length: int = 512,
86
+ window_fn: str = "hann_window",
87
+ wkwargs: Optional[Dict] = None,
88
+ power: Optional[int] = None,
89
+ normalized: bool = True,
90
+ center: bool = True,
91
+ pad_mode: str = "constant",
92
+ onesided: bool = True,
93
+ ):
94
+
95
+ assert power is None
96
+
97
+ window_fn = torch.__dict__[window_fn]
98
+
99
+ self.stft = ta.transforms.Spectrogram(
100
+ n_fft=n_fft,
101
+ win_length=win_length,
102
+ hop_length=hop_length,
103
+ pad_mode=pad_mode,
104
+ pad=0,
105
+ window_fn=window_fn,
106
+ wkwargs=wkwargs,
107
+ power=power,
108
+ normalized=normalized,
109
+ center=center,
110
+ onesided=onesided,
111
+ )
112
+
113
+ self.istft = ta.transforms.InverseSpectrogram(
114
+ n_fft=n_fft,
115
+ win_length=win_length,
116
+ hop_length=hop_length,
117
+ pad_mode=pad_mode,
118
+ pad=0,
119
+ window_fn=window_fn,
120
+ wkwargs=wkwargs,
121
+ normalized=normalized,
122
+ center=center,
123
+ onesided=onesided,
124
+ )
125
+
126
+ def instantiate_bandsplit(
127
+ self,
128
+ in_channel: int,
129
+ band_type: str = "musical",
130
+ n_bands: int = 64,
131
+ require_no_overlap: bool = False,
132
+ require_no_gap: bool = True,
133
+ normalize_channel_independently: bool = False,
134
+ treat_channel_as_feature: bool = True,
135
+ emb_dim: int = 128,
136
+ n_fft: int = 2048,
137
+ fs: int = 44100,
138
+ ):
139
+
140
+ assert band_type == "musical"
141
+
142
+ self.band_specs = MusicalBandsplitSpecification(
143
+ nfft=n_fft, fs=fs, n_bands=n_bands
144
+ )
145
+
146
+ self.band_split = BandSplitModule(
147
+ in_channel=in_channel,
148
+ band_specs=self.band_specs.get_band_specs(),
149
+ require_no_overlap=require_no_overlap,
150
+ require_no_gap=require_no_gap,
151
+ normalize_channel_independently=normalize_channel_independently,
152
+ treat_channel_as_feature=treat_channel_as_feature,
153
+ emb_dim=emb_dim,
154
+ )
155
+
156
+ def instantiate_tf_modelling(
157
+ self,
158
+ n_sqm_modules: int = 12,
159
+ emb_dim: int = 128,
160
+ rnn_dim: int = 256,
161
+ bidirectional: bool = True,
162
+ rnn_type: str = "LSTM",
163
+ ):
164
+ self.tf_model = SeqBandModellingModule(
165
+ n_modules=n_sqm_modules,
166
+ emb_dim=emb_dim,
167
+ rnn_dim=rnn_dim,
168
+ bidirectional=bidirectional,
169
+ rnn_type=rnn_type,
170
+ )
171
+
172
+ def mask(self, x, m):
173
+ return x * m
174
+
175
+ def forward(self, batch: InputType, mode: OperationMode = OperationMode.TRAIN):
176
+
177
+ with torch.no_grad():
178
+ x = self.stft(batch.mixture.audio)
179
+ batch.mixture.spectrogram = x
180
+
181
+ if "sources" in batch.keys():
182
+ for stem in batch.sources.keys():
183
+ s = batch.sources[stem].audio
184
+ s = self.stft(s)
185
+ batch.sources[stem].spectrogram = s
186
+
187
+ batch = self.separate(batch)
188
+
189
+ return batch
190
+
191
+ def encode(self, batch):
192
+ x = batch.mixture.spectrogram
193
+ length = batch.mixture.audio.shape[-1]
194
+
195
+ z = self.band_split(x) # (batch, emb_dim, n_band, n_time)
196
+ q = self.tf_model(z) # (batch, emb_dim, n_band, n_time)
197
+
198
+ return x, q, length
199
+
200
+ def separate(self, batch):
201
+ raise NotImplementedError
202
+
203
+
204
+ class Bandit(BaseBandit):
205
+ def __init__(
206
+ self,
207
+ in_channel: int,
208
+ stems: List[str],
209
+ band_type: str = "musical",
210
+ n_bands: int = 64,
211
+ require_no_overlap: bool = False,
212
+ require_no_gap: bool = True,
213
+ normalize_channel_independently: bool = False,
214
+ treat_channel_as_feature: bool = True,
215
+ n_sqm_modules: int = 12,
216
+ emb_dim: int = 128,
217
+ rnn_dim: int = 256,
218
+ bidirectional: bool = True,
219
+ rnn_type: str = "LSTM",
220
+ mlp_dim: int = 512,
221
+ hidden_activation: str = "Tanh",
222
+ hidden_activation_kwargs: Dict | None = None,
223
+ complex_mask: bool = True,
224
+ use_freq_weights: bool = True,
225
+ n_fft: int = 2048,
226
+ win_length: int | None = 2048,
227
+ hop_length: int = 512,
228
+ window_fn: str = "hann_window",
229
+ wkwargs: Dict | None = None,
230
+ power: int | None = None,
231
+ center: bool = True,
232
+ normalized: bool = True,
233
+ pad_mode: str = "constant",
234
+ onesided: bool = True,
235
+ fs: int = 44100,
236
+ ):
237
+ super().__init__(
238
+ in_channel=in_channel,
239
+ band_type=band_type,
240
+ n_bands=n_bands,
241
+ require_no_overlap=require_no_overlap,
242
+ require_no_gap=require_no_gap,
243
+ normalize_channel_independently=normalize_channel_independently,
244
+ treat_channel_as_feature=treat_channel_as_feature,
245
+ n_sqm_modules=n_sqm_modules,
246
+ emb_dim=emb_dim,
247
+ rnn_dim=rnn_dim,
248
+ bidirectional=bidirectional,
249
+ rnn_type=rnn_type,
250
+ n_fft=n_fft,
251
+ win_length=win_length,
252
+ hop_length=hop_length,
253
+ window_fn=window_fn,
254
+ wkwargs=wkwargs,
255
+ power=power,
256
+ center=center,
257
+ normalized=normalized,
258
+ pad_mode=pad_mode,
259
+ onesided=onesided,
260
+ fs=fs,
261
+ )
262
+
263
+ self.instantiate_mask_estim(
264
+ in_channel=in_channel,
265
+ stems=stems,
266
+ emb_dim=emb_dim,
267
+ mlp_dim=mlp_dim,
268
+ hidden_activation=hidden_activation,
269
+ hidden_activation_kwargs=hidden_activation_kwargs,
270
+ complex_mask=complex_mask,
271
+ n_freq=n_fft // 2 + 1,
272
+ use_freq_weights=use_freq_weights,
273
+ )
274
+
275
+ def instantiate_mask_estim(
276
+ self,
277
+ in_channel: int,
278
+ stems: List[str],
279
+ emb_dim: int,
280
+ mlp_dim: int,
281
+ hidden_activation: str,
282
+ hidden_activation_kwargs: Optional[Dict] = None,
283
+ complex_mask: bool = True,
284
+ n_freq: Optional[int] = None,
285
+ use_freq_weights: bool = True,
286
+ ):
287
+ if hidden_activation_kwargs is None:
288
+ hidden_activation_kwargs = {}
289
+
290
+ assert n_freq is not None
291
+
292
+ self.mask_estim = nn.ModuleDict(
293
+ {
294
+ stem: OverlappingMaskEstimationModule(
295
+ band_specs=self.band_specs.get_band_specs(),
296
+ freq_weights=self.band_specs.get_freq_weights(),
297
+ n_freq=n_freq,
298
+ emb_dim=emb_dim,
299
+ mlp_dim=mlp_dim,
300
+ in_channel=in_channel,
301
+ hidden_activation=hidden_activation,
302
+ hidden_activation_kwargs=hidden_activation_kwargs,
303
+ complex_mask=complex_mask,
304
+ use_freq_weights=use_freq_weights,
305
+ )
306
+ for stem in stems
307
+ }
308
+ )
309
+
310
+ def separate(self, batch):
311
+
312
+ x, q, length = self.encode(batch)
313
+
314
+ for stem, mem in self.mask_estim.items():
315
+ m = mem(q)
316
+ s = self.mask(x, m)
317
+ s = torch.reshape(s, x.shape)
318
+ batch.estimates[stem] = SimpleishNamespace(
319
+ audio=self.istft(s, length), spectrogram=s
320
+ )
321
+
322
+ return batch
323
+
324
+
325
+ class BaseConditionedBandit(BaseBandit):
326
+ query_encoder: nn.Module
327
+
328
+ def __init__(
329
+ self,
330
+ in_channel: int,
331
+ band_type: str = "musical",
332
+ n_bands: int = 64,
333
+ require_no_overlap: bool = False,
334
+ require_no_gap: bool = True,
335
+ normalize_channel_independently: bool = False,
336
+ treat_channel_as_feature: bool = True,
337
+ n_sqm_modules: int = 12,
338
+ emb_dim: int = 128,
339
+ rnn_dim: int = 256,
340
+ bidirectional: bool = True,
341
+ rnn_type: str = "LSTM",
342
+ mlp_dim: int = 512,
343
+ hidden_activation: str = "Tanh",
344
+ hidden_activation_kwargs: Dict | None = None,
345
+ complex_mask: bool = True,
346
+ use_freq_weights: bool = True,
347
+ n_fft: int = 2048,
348
+ win_length: int | None = 2048,
349
+ hop_length: int = 512,
350
+ window_fn: str = "hann_window",
351
+ wkwargs: Dict | None = None,
352
+ power: int | None = None,
353
+ center: bool = True,
354
+ normalized: bool = True,
355
+ pad_mode: str = "constant",
356
+ onesided: bool = True,
357
+ fs: int = 44100,
358
+ ):
359
+ super().__init__(
360
+ in_channel=in_channel,
361
+ band_type=band_type,
362
+ n_bands=n_bands,
363
+ require_no_overlap=require_no_overlap,
364
+ require_no_gap=require_no_gap,
365
+ normalize_channel_independently=normalize_channel_independently,
366
+ treat_channel_as_feature=treat_channel_as_feature,
367
+ n_sqm_modules=n_sqm_modules,
368
+ emb_dim=emb_dim,
369
+ rnn_dim=rnn_dim,
370
+ bidirectional=bidirectional,
371
+ rnn_type=rnn_type,
372
+ n_fft=n_fft,
373
+ win_length=win_length,
374
+ hop_length=hop_length,
375
+ window_fn=window_fn,
376
+ wkwargs=wkwargs,
377
+ power=power,
378
+ center=center,
379
+ normalized=normalized,
380
+ pad_mode=pad_mode,
381
+ onesided=onesided,
382
+ fs=fs,
383
+ )
384
+
385
+ self.instantiate_mask_estim(
386
+ in_channel=in_channel,
387
+ emb_dim=emb_dim,
388
+ mlp_dim=mlp_dim,
389
+ hidden_activation=hidden_activation,
390
+ hidden_activation_kwargs=hidden_activation_kwargs,
391
+ complex_mask=complex_mask,
392
+ n_freq=n_fft // 2 + 1,
393
+ use_freq_weights=use_freq_weights,
394
+ )
395
+
396
+ def instantiate_mask_estim(
397
+ self,
398
+ in_channel: int,
399
+ emb_dim: int,
400
+ mlp_dim: int,
401
+ hidden_activation: str,
402
+ hidden_activation_kwargs: Optional[Dict] = None,
403
+ complex_mask: bool = True,
404
+ n_freq: Optional[int] = None,
405
+ use_freq_weights: bool = True,
406
+ ):
407
+ if hidden_activation_kwargs is None:
408
+ hidden_activation_kwargs = {}
409
+
410
+ assert n_freq is not None
411
+
412
+ self.mask_estim = OverlappingMaskEstimationModule(
413
+ band_specs=self.band_specs.get_band_specs(),
414
+ freq_weights=self.band_specs.get_freq_weights(),
415
+ n_freq=n_freq,
416
+ emb_dim=emb_dim,
417
+ mlp_dim=mlp_dim,
418
+ in_channel=in_channel,
419
+ hidden_activation=hidden_activation,
420
+ hidden_activation_kwargs=hidden_activation_kwargs,
421
+ complex_mask=complex_mask,
422
+ use_freq_weights=use_freq_weights,
423
+ )
424
+
425
+ def separate(self, batch):
426
+
427
+ x, q, length = self.encode(batch)
428
+
429
+ q = self.adapt_query(q, batch)
430
+
431
+ m = self.mask_estim(q)
432
+ s = self.mask(x, m)
433
+ s = torch.reshape(s, x.shape)
434
+ batch.estimates["target"] = SimpleishNamespace(
435
+ audio=self.istft(s, length), spectrogram=s
436
+ )
437
+
438
+ return batch
439
+
440
+ def adapt_query(self, q, batch):
441
+ raise NotImplementedError
442
+
443
+
444
+ class PasstFiLMConditionedBandit(BaseConditionedBandit):
445
+
446
+ def __init__(
447
+ self,
448
+ in_channel: int,
449
+ band_type: str = "musical",
450
+ n_bands: int = 64,
451
+ additive_film: bool = True,
452
+ multiplicative_film: bool = True,
453
+ film_depth: int = 2,
454
+ require_no_overlap: bool = False,
455
+ require_no_gap: bool = True,
456
+ normalize_channel_independently: bool = False,
457
+ treat_channel_as_feature: bool = True,
458
+ n_sqm_modules: int = 12,
459
+ emb_dim: int = 128,
460
+ rnn_dim: int = 256,
461
+ bidirectional: bool = True,
462
+ rnn_type: str = "LSTM",
463
+ mlp_dim: int = 512,
464
+ hidden_activation: str = "Tanh",
465
+ hidden_activation_kwargs: Dict | None = None,
466
+ complex_mask: bool = True,
467
+ use_freq_weights: bool = True,
468
+ n_fft: int = 2048,
469
+ win_length: int | None = 2048,
470
+ hop_length: int = 512,
471
+ window_fn: str = "hann_window",
472
+ wkwargs: Dict | None = None,
473
+ power: int | None = None,
474
+ center: bool = True,
475
+ normalized: bool = True,
476
+ pad_mode: str = "constant",
477
+ onesided: bool = True,
478
+ fs: int = 44100,
479
+ pretrain_encoder = None,
480
+ freeze_encoder = False
481
+ ):
482
+ super().__init__(
483
+ in_channel=in_channel,
484
+ band_type=band_type,
485
+ n_bands=n_bands,
486
+ require_no_overlap=require_no_overlap,
487
+ require_no_gap=require_no_gap,
488
+ normalize_channel_independently=normalize_channel_independently,
489
+ treat_channel_as_feature=treat_channel_as_feature,
490
+ n_sqm_modules=n_sqm_modules,
491
+ emb_dim=emb_dim,
492
+ rnn_dim=rnn_dim,
493
+ bidirectional=bidirectional,
494
+ rnn_type=rnn_type,
495
+ mlp_dim=mlp_dim,
496
+ hidden_activation=hidden_activation,
497
+ hidden_activation_kwargs=hidden_activation_kwargs,
498
+ complex_mask=complex_mask,
499
+ use_freq_weights=use_freq_weights,
500
+ n_fft=n_fft,
501
+ win_length=win_length,
502
+ hop_length=hop_length,
503
+ window_fn=window_fn,
504
+ wkwargs=wkwargs,
505
+ power=power,
506
+ center=center,
507
+ normalized=normalized,
508
+ pad_mode=pad_mode,
509
+ onesided=onesided,
510
+ fs=fs,
511
+ )
512
+
513
+ self.query_encoder = Passt(
514
+ original_fs=fs,
515
+ passt_fs=32000,
516
+ )
517
+
518
+ self.film = FiLM(
519
+ self.query_encoder.PASST_EMB_DIM,
520
+ emb_dim,
521
+ additive=additive_film,
522
+ multiplicative=multiplicative_film,
523
+ depth=film_depth,
524
+ )
525
+
526
+ if pretrain_encoder is not None:
527
+ self.load_pretrained_encoder(pretrain_encoder)
528
+
529
+ for p in self.band_split.parameters():
530
+ p.requires_grad = not freeze_encoder
531
+
532
+ for p in self.tf_model.parameters():
533
+ p.requires_grad = not freeze_encoder
534
+
535
+
536
+
537
+ def load_pretrained_encoder(self, path):
538
+
539
+ state_dict = torch.load(path, map_location="cpu")["state_dict"]
540
+
541
+ state_dict_ = {k.replace("model.", "") if k.startswith("model.") else k: v for k, v in state_dict.items()}
542
+
543
+ state_dict = {}
544
+
545
+ for k, v in state_dict_.items():
546
+ if "mask_estim" in k:
547
+ continue
548
+
549
+ if "tf_seqband" in k:
550
+ k = k.replace("tf_seqband", "tf_model.seqband")
551
+
552
+ state_dict[k] = v
553
+
554
+
555
+ res = self.load_state_dict(state_dict, strict=False)
556
+
557
+ for k in res.unexpected_keys:
558
+ if "mask_estim" in k:
559
+ continue
560
+ print(f"Unexpected key: {k}")
561
+
562
+ for k in res.missing_keys:
563
+ print(f"Missing key: {k}")
564
+ for kw in ["band_split", "tf_model"]:
565
+ if kw in k:
566
+ raise ValueError(f"Missing key: {k}")
567
+
568
+ for kw in ["mask_estim", "query_encoder"]:
569
+ if kw in k:
570
+ continue
571
+
572
+
573
+
574
+ def adapt_query(self, q, batch):
575
+
576
+ w = self.query_encoder(batch.query.audio)
577
+ q = torch.permute(q, (0, 3, 1, 2)) # (batch, n_band, n_time, emb_dim) -> (batch, emb_dim, n_band, n_time)
578
+ q = self.film(q, w)
579
+ q = torch.permute(q, (0, 2, 3, 1)) # -> (batch, n_band, n_time, emb_dim)
580
+
581
+ return q
582
+
583
+
584
+ def optimized_forward(self, batch: InputType, mode: OperationMode = OperationMode.TRAIN):
585
+
586
+ with torch.no_grad():
587
+ x = self.stft(batch.mixture.audio)
588
+ batch.mixture.spectrogram = x
589
+
590
+ if "sources" in batch.keys():
591
+ for stem in batch.sources.keys():
592
+ s = batch.sources[stem].audio
593
+ s = self.stft(s)
594
+ batch.sources[stem].spectrogram = s
595
+
596
+ batch = self.optimized_separate(batch)
597
+
598
+ return batch
599
+
600
+
601
+ def optimized_separate(self, batch):
602
+
603
+ x, q, length = self.encode(batch)
604
+
605
+ for stem, query in batch.query.items():
606
+
607
+ batch_ = SimpleishNamespace(**batch.__dict__)
608
+ batch_.query = query
609
+
610
+ q = self.adapt_query(q, batch_)
611
+
612
+ m = self.mask_estim(q)
613
+ s = self.mask(x, m)
614
+ s = torch.reshape(s, x.shape)
615
+ batch.estimates[stem] = SimpleishNamespace(
616
+ audio=self.istft(s, length), spectrogram=s
617
+ )
618
+
619
+ return batch
core/models/e2e/bandit/bandsplit.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Tuple
2
+
3
+ import torch
4
+ from torch import nn
5
+
6
+ from core.models.e2e.bandit.utils import band_widths_from_specs, check_no_gap, check_no_overlap, check_nonzero_bandwidth
7
+
8
+
9
+
10
+ class NormFC(nn.Module):
11
+ def __init__(
12
+ self,
13
+ emb_dim: int,
14
+ bandwidth: int,
15
+ in_channel: int,
16
+ normalize_channel_independently: bool = False,
17
+ treat_channel_as_feature: bool = True,
18
+ ) -> None:
19
+ super().__init__()
20
+
21
+ self.treat_channel_as_feature = treat_channel_as_feature
22
+
23
+ if normalize_channel_independently:
24
+ raise NotImplementedError
25
+
26
+ reim = 2
27
+
28
+ self.norm = nn.LayerNorm(in_channel * bandwidth * reim)
29
+
30
+ fc_in = bandwidth * reim
31
+
32
+ if treat_channel_as_feature:
33
+ fc_in *= in_channel
34
+ else:
35
+ assert emb_dim % in_channel == 0
36
+ emb_dim = emb_dim // in_channel
37
+
38
+ self.fc = nn.Linear(fc_in, emb_dim)
39
+
40
+ def forward(self, xb):
41
+ # xb = (batch, n_time, in_chan, reim * band_width)
42
+
43
+ batch, n_time, in_chan, ribw = xb.shape
44
+ xb = self.norm(xb.reshape(batch, n_time, in_chan * ribw))
45
+ # (batch, n_time, in_chan * reim * band_width)
46
+
47
+ if not self.treat_channel_as_feature:
48
+ xb = xb.reshape(batch, n_time, in_chan, ribw)
49
+ # (batch, n_time, in_chan, reim * band_width)
50
+
51
+ zb = self.fc(xb)
52
+ # (batch, n_time, emb_dim)
53
+ # OR
54
+ # (batch, n_time, in_chan, emb_dim_per_chan)
55
+
56
+ if not self.treat_channel_as_feature:
57
+ batch, n_time, in_chan, emb_dim_per_chan = zb.shape
58
+ # (batch, n_time, in_chan, emb_dim_per_chan)
59
+ zb = zb.reshape((batch, n_time, in_chan * emb_dim_per_chan))
60
+
61
+ return zb # (batch, n_time, emb_dim)
62
+
63
+
64
+ class BandSplitModule(nn.Module):
65
+ def __init__(
66
+ self,
67
+ band_specs: List[Tuple[float, float]],
68
+ emb_dim: int,
69
+ in_channel: int,
70
+ require_no_overlap: bool = False,
71
+ require_no_gap: bool = True,
72
+ normalize_channel_independently: bool = False,
73
+ treat_channel_as_feature: bool = True,
74
+ ) -> None:
75
+ super().__init__()
76
+
77
+ check_nonzero_bandwidth(band_specs)
78
+
79
+ if require_no_gap:
80
+ check_no_gap(band_specs)
81
+
82
+ if require_no_overlap:
83
+ check_no_overlap(band_specs)
84
+
85
+ self.band_specs = band_specs
86
+ # list of [fstart, fend) in index.
87
+ # Note that fend is exclusive.
88
+ self.band_widths = band_widths_from_specs(band_specs)
89
+ self.n_bands = len(band_specs)
90
+ self.emb_dim = emb_dim
91
+
92
+ self.norm_fc_modules = nn.ModuleList(
93
+ [ # type: ignore
94
+ (
95
+ NormFC(
96
+ emb_dim=emb_dim,
97
+ bandwidth=bw,
98
+ in_channel=in_channel,
99
+ normalize_channel_independently=normalize_channel_independently,
100
+ treat_channel_as_feature=treat_channel_as_feature,
101
+ )
102
+ )
103
+ for bw in self.band_widths
104
+ ]
105
+ )
106
+
107
+ def forward(self, x: torch.Tensor):
108
+ # x = complex spectrogram (batch, in_chan, n_freq, n_time)
109
+
110
+ batch, in_chan, _, n_time = x.shape
111
+
112
+ z = torch.zeros(
113
+ size=(batch, self.n_bands, n_time, self.emb_dim),
114
+ device=x.device
115
+ )
116
+
117
+ xr = torch.view_as_real(x) # batch, in_chan, n_freq, n_time, 2
118
+ xr = torch.permute(
119
+ xr,
120
+ (0, 3, 1, 4, 2)
121
+ ) # batch, n_time, in_chan, 2, n_freq
122
+ batch, n_time, in_chan, reim, band_width = xr.shape
123
+ for i, nfm in enumerate(self.norm_fc_modules):
124
+ # print(f"bandsplit/band{i:02d}")
125
+ fstart, fend = self.band_specs[i]
126
+ xb = xr[..., fstart:fend]
127
+ # (batch, n_time, in_chan, reim, band_width)
128
+ xb = torch.reshape(xb, (batch, n_time, in_chan, -1))
129
+ # (batch, n_time, in_chan, reim * band_width)
130
+ # z.append(nfm(xb)) # (batch, n_time, emb_dim)
131
+ z[:, i, :, :] = nfm(xb.contiguous())
132
+
133
+ # z = torch.stack(z, dim=1)
134
+
135
+ return z
core/models/e2e/bandit/maskestim.py ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import warnings
2
+ from typing import Dict, List, Optional, Tuple, Type
3
+
4
+ import torch
5
+ from torch import nn
6
+ from torch.nn.modules import activation
7
+
8
+ from core.models.e2e.bandit.utils import (
9
+ band_widths_from_specs,
10
+ check_no_gap,
11
+ check_no_overlap,
12
+ check_nonzero_bandwidth,
13
+ )
14
+
15
+
16
+ class BaseNormMLP(nn.Module):
17
+ def __init__(
18
+ self,
19
+ emb_dim: int,
20
+ mlp_dim: int,
21
+ bandwidth: int,
22
+ in_channel: Optional[int],
23
+ hidden_activation: str = "Tanh",
24
+ hidden_activation_kwargs=None,
25
+ complex_mask: bool = True, ):
26
+
27
+ super().__init__()
28
+ if hidden_activation_kwargs is None:
29
+ hidden_activation_kwargs = {}
30
+ self.hidden_activation_kwargs = hidden_activation_kwargs
31
+ self.norm = nn.LayerNorm(emb_dim)
32
+ self.hidden = torch.jit.script(nn.Sequential(
33
+ nn.Linear(in_features=emb_dim, out_features=mlp_dim),
34
+ activation.__dict__[hidden_activation](
35
+ **self.hidden_activation_kwargs
36
+ ),
37
+ ))
38
+
39
+ self.bandwidth = bandwidth
40
+ self.in_channel = in_channel
41
+
42
+ self.complex_mask = complex_mask
43
+ self.reim = 2 if complex_mask else 1
44
+ self.glu_mult = 2
45
+
46
+
47
+ class NormMLP(BaseNormMLP):
48
+ def __init__(
49
+ self,
50
+ emb_dim: int,
51
+ mlp_dim: int,
52
+ bandwidth: int,
53
+ in_channel: Optional[int],
54
+ hidden_activation: str = "Tanh",
55
+ hidden_activation_kwargs=None,
56
+ complex_mask: bool = True,
57
+ ) -> None:
58
+ super().__init__(
59
+ emb_dim=emb_dim,
60
+ mlp_dim=mlp_dim,
61
+ bandwidth=bandwidth,
62
+ in_channel=in_channel,
63
+ hidden_activation=hidden_activation,
64
+ hidden_activation_kwargs=hidden_activation_kwargs,
65
+ complex_mask=complex_mask,
66
+ )
67
+
68
+ self.output = torch.jit.script(
69
+ nn.Sequential(
70
+ nn.Linear(
71
+ in_features=mlp_dim,
72
+ out_features=bandwidth * in_channel * self.reim * 2,
73
+ ),
74
+ nn.GLU(dim=-1),
75
+ )
76
+ )
77
+
78
+ def reshape_output(self, mb):
79
+ # print(mb.shape)
80
+ batch, n_time, _ = mb.shape
81
+ if self.complex_mask:
82
+ mb = mb.reshape(
83
+ batch,
84
+ n_time,
85
+ self.in_channel,
86
+ self.bandwidth,
87
+ self.reim
88
+ ).contiguous()
89
+ # print(mb.shape)
90
+ mb = torch.view_as_complex(
91
+ mb
92
+ ) # (batch, n_time, in_channel, bandwidth)
93
+ else:
94
+ mb = mb.reshape(batch, n_time, self.in_channel, self.bandwidth)
95
+
96
+ mb = torch.permute(
97
+ mb,
98
+ (0, 2, 3, 1)
99
+ ) # (batch, in_channel, bandwidth, n_time)
100
+
101
+ return mb
102
+
103
+ def forward(self, qb):
104
+ # qb = (batch, n_time, emb_dim)
105
+
106
+ # if torch.any(torch.isnan(qb)):
107
+ # raise ValueError("qb0")
108
+
109
+
110
+ qb = self.norm(qb) # (batch, n_time, emb_dim)
111
+
112
+ # if torch.any(torch.isnan(qb)):
113
+ # raise ValueError("qb1")
114
+
115
+ qb = self.hidden(qb) # (batch, n_time, mlp_dim)
116
+ # if torch.any(torch.isnan(qb)):
117
+ # raise ValueError("qb2")
118
+ mb = self.output(qb) # (batch, n_time, bandwidth * in_channel * reim)
119
+ # if torch.any(torch.isnan(qb)):
120
+ # raise ValueError("mb")
121
+ mb = self.reshape_output(mb) # (batch, in_channel, bandwidth, n_time)
122
+
123
+ return mb
124
+
125
+
126
+ # class MultAddNormMLP(NormMLP):
127
+ # def __init__(self, emb_dim: int, mlp_dim: int, bandwidth: int, in_channel: int | None, hidden_activation: str = "Tanh", hidden_activation_kwargs=None, complex_mask: bool = True) -> None:
128
+ # super().__init__(emb_dim, mlp_dim, bandwidth, in_channel, hidden_activation, hidden_activation_kwargs, complex_mask)
129
+
130
+ # self.output2 = torch.jit.script(
131
+ # nn.Sequential(
132
+ # nn.Linear(
133
+ # in_features=mlp_dim,
134
+ # out_features=bandwidth * in_channel * self.reim * 2,
135
+ # ),
136
+ # nn.GLU(dim=-1),
137
+ # )
138
+ # )
139
+
140
+ # def forward(self, qb):
141
+
142
+ # qb = self.norm(qb) # (batch, n_time, emb_dim)
143
+ # qb = self.hidden(qb) # (batch, n_time, mlp_dim)
144
+ # mmb = self.output(qb) # (batch, n_time, bandwidth * in_channel * reim)
145
+ # mmb = self.reshape_output(mmb) # (batch, in_channel, bandwidth, n_time)
146
+ # amb = self.output2(qb) # (batch, n_time, bandwidth * in_channel * reim)
147
+ # amb = self.reshape_output(amb) # (batch, in_channel, bandwidth, n_time)
148
+
149
+ # return mmb, amb
150
+
151
+
152
+ class MaskEstimationModuleSuperBase(nn.Module):
153
+ pass
154
+
155
+
156
+ class MaskEstimationModuleBase(MaskEstimationModuleSuperBase):
157
+ def __init__(
158
+ self,
159
+ band_specs: List[Tuple[float, float]],
160
+ emb_dim: int,
161
+ mlp_dim: int,
162
+ in_channel: Optional[int],
163
+ hidden_activation: str = "Tanh",
164
+ hidden_activation_kwargs: Dict = None,
165
+ complex_mask: bool = True,
166
+ norm_mlp_cls: Type[nn.Module] = NormMLP,
167
+ norm_mlp_kwargs: Dict = None,
168
+ ) -> None:
169
+ super().__init__()
170
+
171
+ self.band_widths = band_widths_from_specs(band_specs)
172
+ self.n_bands = len(band_specs)
173
+
174
+ if hidden_activation_kwargs is None:
175
+ hidden_activation_kwargs = {}
176
+
177
+ if norm_mlp_kwargs is None:
178
+ norm_mlp_kwargs = {}
179
+
180
+ self.norm_mlp = nn.ModuleList(
181
+ [
182
+ (
183
+ norm_mlp_cls(
184
+ bandwidth=self.band_widths[b],
185
+ emb_dim=emb_dim,
186
+ mlp_dim=mlp_dim,
187
+ in_channel=in_channel,
188
+ hidden_activation=hidden_activation,
189
+ hidden_activation_kwargs=hidden_activation_kwargs,
190
+ complex_mask=complex_mask,
191
+ **norm_mlp_kwargs,
192
+ )
193
+ )
194
+ for b in range(self.n_bands)
195
+ ]
196
+ )
197
+
198
+ def compute_masks(self, q):
199
+ batch, n_bands, n_time, emb_dim = q.shape
200
+
201
+ masks = []
202
+
203
+ for b, nmlp in enumerate(self.norm_mlp):
204
+ # print(f"maskestim/{b:02d}")
205
+ qb = q[:, b, :, :]
206
+ mb = nmlp(qb)
207
+ masks.append(mb)
208
+
209
+ return masks
210
+
211
+
212
+
213
+ class OverlappingMaskEstimationModule(MaskEstimationModuleBase):
214
+ def __init__(
215
+ self,
216
+ in_channel: int,
217
+ band_specs: List[Tuple[float, float]],
218
+ freq_weights: List[torch.Tensor],
219
+ n_freq: int,
220
+ emb_dim: int,
221
+ mlp_dim: int,
222
+ cond_dim: int = 0,
223
+ hidden_activation: str = "Tanh",
224
+ hidden_activation_kwargs: Dict = None,
225
+ complex_mask: bool = True,
226
+ norm_mlp_cls: Type[nn.Module] = NormMLP,
227
+ norm_mlp_kwargs: Dict = None,
228
+ use_freq_weights: bool = True,
229
+ ) -> None:
230
+ check_nonzero_bandwidth(band_specs)
231
+ check_no_gap(band_specs)
232
+
233
+ # if cond_dim > 0:
234
+ # raise NotImplementedError
235
+
236
+ super().__init__(
237
+ band_specs=band_specs,
238
+ emb_dim=emb_dim + cond_dim,
239
+ mlp_dim=mlp_dim,
240
+ in_channel=in_channel,
241
+ hidden_activation=hidden_activation,
242
+ hidden_activation_kwargs=hidden_activation_kwargs,
243
+ complex_mask=complex_mask,
244
+ norm_mlp_cls=norm_mlp_cls,
245
+ norm_mlp_kwargs=norm_mlp_kwargs,
246
+ )
247
+
248
+ self.n_freq = n_freq
249
+ self.band_specs = band_specs
250
+ self.in_channel = in_channel
251
+
252
+ if freq_weights is not None:
253
+ for i, fw in enumerate(freq_weights):
254
+ self.register_buffer(f"freq_weights/{i}", fw)
255
+
256
+ self.use_freq_weights = use_freq_weights
257
+ else:
258
+ self.use_freq_weights = False
259
+
260
+ self.cond_dim = cond_dim
261
+
262
+ def forward(self, q, cond=None):
263
+ # q = (batch, n_bands, n_time, emb_dim)
264
+
265
+ batch, n_bands, n_time, emb_dim = q.shape
266
+
267
+ if cond is not None:
268
+ print(cond)
269
+ if cond.ndim == 2:
270
+ cond = cond[:, None, None, :].expand(-1, n_bands, n_time, -1)
271
+ elif cond.ndim == 3:
272
+ assert cond.shape[1] == n_time
273
+ else:
274
+ raise ValueError(f"Invalid cond shape: {cond.shape}")
275
+
276
+ q = torch.cat([q, cond], dim=-1)
277
+ elif self.cond_dim > 0:
278
+ cond = torch.ones(
279
+ (batch, n_bands, n_time, self.cond_dim),
280
+ device=q.device,
281
+ dtype=q.dtype,
282
+ )
283
+ q = torch.cat([q, cond], dim=-1)
284
+ else:
285
+ pass
286
+
287
+ mask_list = self.compute_masks(
288
+ q
289
+ ) # [n_bands * (batch, in_channel, bandwidth, n_time)]
290
+
291
+ masks = torch.zeros(
292
+ (batch, self.in_channel, self.n_freq, n_time),
293
+ device=q.device,
294
+ dtype=mask_list[0].dtype,
295
+ )
296
+
297
+ for im, mask in enumerate(mask_list):
298
+ fstart, fend = self.band_specs[im]
299
+ if self.use_freq_weights:
300
+ fw = self.get_buffer(f"freq_weights/{im}")[:, None]
301
+ mask = mask * fw
302
+ masks[:, :, fstart:fend, :] += mask
303
+
304
+ return masks
305
+
306
+
307
+ class MaskEstimationModule(OverlappingMaskEstimationModule):
308
+ def __init__(
309
+ self,
310
+ band_specs: List[Tuple[float, float]],
311
+ emb_dim: int,
312
+ mlp_dim: int,
313
+ in_channel: Optional[int],
314
+ hidden_activation: str = "Tanh",
315
+ hidden_activation_kwargs: Dict = None,
316
+ complex_mask: bool = True,
317
+ **kwargs,
318
+ ) -> None:
319
+ check_nonzero_bandwidth(band_specs)
320
+ check_no_gap(band_specs)
321
+ check_no_overlap(band_specs)
322
+ super().__init__(
323
+ in_channel=in_channel,
324
+ band_specs=band_specs,
325
+ freq_weights=None,
326
+ n_freq=None,
327
+ emb_dim=emb_dim,
328
+ mlp_dim=mlp_dim,
329
+ hidden_activation=hidden_activation,
330
+ hidden_activation_kwargs=hidden_activation_kwargs,
331
+ complex_mask=complex_mask,
332
+ )
333
+
334
+ def forward(self, q, cond=None):
335
+ # q = (batch, n_bands, n_time, emb_dim)
336
+
337
+ masks = self.compute_masks(
338
+ q
339
+ ) # [n_bands * (batch, in_channel, bandwidth, n_time)]
340
+
341
+ # TODO: currently this requires band specs to have no gap and no overlap
342
+ masks = torch.concat(
343
+ masks,
344
+ dim=2
345
+ ) # (batch, in_channel, n_freq, n_time)
346
+
347
+ return masks
core/models/e2e/bandit/tfmodel.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import warnings
2
+
3
+ import torch
4
+ from torch import nn
5
+ from torch.nn import functional as F
6
+ from torch.nn.modules import rnn
7
+
8
+ import torch.backends.cuda
9
+
10
+
11
+ class TimeFrequencyModellingModule(nn.Module):
12
+ def __init__(self) -> None:
13
+ super().__init__()
14
+
15
+
16
+ class ResidualRNN(nn.Module):
17
+ def __init__(
18
+ self,
19
+ emb_dim: int,
20
+ rnn_dim: int,
21
+ bidirectional: bool = True,
22
+ rnn_type: str = "LSTM",
23
+ use_batch_trick: bool = True,
24
+ use_layer_norm: bool = True,
25
+ ) -> None:
26
+ # n_group is the size of the 2nd dim
27
+ super().__init__()
28
+
29
+ self.use_layer_norm = use_layer_norm
30
+ if use_layer_norm:
31
+ self.norm = nn.LayerNorm(emb_dim)
32
+ else:
33
+ self.norm = nn.GroupNorm(num_groups=emb_dim, num_channels=emb_dim)
34
+
35
+ self.rnn = rnn.__dict__[rnn_type](
36
+ input_size=emb_dim,
37
+ hidden_size=rnn_dim,
38
+ num_layers=1,
39
+ batch_first=True,
40
+ bidirectional=bidirectional,
41
+ )
42
+
43
+ self.fc = nn.Linear(
44
+ in_features=rnn_dim * (2 if bidirectional else 1),
45
+ out_features=emb_dim
46
+ )
47
+
48
+ self.use_batch_trick = use_batch_trick
49
+ if not self.use_batch_trick:
50
+ warnings.warn("NOT USING BATCH TRICK IS EXTREMELY SLOW!!")
51
+
52
+ def forward(self, z):
53
+ # z = (batch, n_uncrossed, n_across, emb_dim)
54
+
55
+ z0 = torch.clone(z)
56
+
57
+ # print(z.device)
58
+
59
+ if self.use_layer_norm:
60
+ z = self.norm(z) # (batch, n_uncrossed, n_across, emb_dim)
61
+ else:
62
+ z = torch.permute(
63
+ z, (0, 3, 1, 2)
64
+ ) # (batch, emb_dim, n_uncrossed, n_across)
65
+
66
+ z = self.norm(z) # (batch, emb_dim, n_uncrossed, n_across)
67
+
68
+ z = torch.permute(
69
+ z, (0, 2, 3, 1)
70
+ ) # (batch, n_uncrossed, n_across, emb_dim)
71
+
72
+ batch, n_uncrossed, n_across, emb_dim = z.shape
73
+
74
+ if self.use_batch_trick:
75
+ z = torch.reshape(z, (batch * n_uncrossed, n_across, emb_dim))
76
+
77
+ z = self.rnn(z.contiguous())[0] # (batch * n_uncrossed, n_across, dir_rnn_dim)
78
+
79
+ z = torch.reshape(z, (batch, n_uncrossed, n_across, -1))
80
+ # (batch, n_uncrossed, n_across, dir_rnn_dim)
81
+ else:
82
+ # Note: this is EXTREMELY SLOW
83
+ zlist = []
84
+ for i in range(n_uncrossed):
85
+ zi = self.rnn(z[:, i, :, :])[0] # (batch, n_across, emb_dim)
86
+ zlist.append(zi)
87
+
88
+ z = torch.stack(
89
+ zlist,
90
+ dim=1
91
+ ) # (batch, n_uncrossed, n_across, dir_rnn_dim)
92
+
93
+ z = self.fc(z) # (batch, n_uncrossed, n_across, emb_dim)
94
+
95
+ z = z + z0
96
+
97
+ return z
98
+
99
+ class SeqBandModellingModule(TimeFrequencyModellingModule):
100
+ def __init__(
101
+ self,
102
+ n_modules: int = 12,
103
+ emb_dim: int = 128,
104
+ rnn_dim: int = 256,
105
+ bidirectional: bool = True,
106
+ rnn_type: str = "LSTM",
107
+ parallel_mode=False,
108
+ ) -> None:
109
+ super().__init__()
110
+ self.seqband = nn.ModuleList([])
111
+
112
+ if parallel_mode:
113
+ for _ in range(n_modules):
114
+ self.seqband.append(
115
+ nn.ModuleList(
116
+ [ResidualRNN(
117
+ emb_dim=emb_dim,
118
+ rnn_dim=rnn_dim,
119
+ bidirectional=bidirectional,
120
+ rnn_type=rnn_type,
121
+ ),
122
+ ResidualRNN(
123
+ emb_dim=emb_dim,
124
+ rnn_dim=rnn_dim,
125
+ bidirectional=bidirectional,
126
+ rnn_type=rnn_type,
127
+ )]
128
+ )
129
+ )
130
+ else:
131
+
132
+ for _ in range(2 * n_modules):
133
+ self.seqband.append(
134
+ ResidualRNN(
135
+ emb_dim=emb_dim,
136
+ rnn_dim=rnn_dim,
137
+ bidirectional=bidirectional,
138
+ rnn_type=rnn_type,
139
+ )
140
+ )
141
+
142
+ self.parallel_mode = parallel_mode
143
+
144
+ def forward(self, z):
145
+ # z = (batch, n_bands, n_time, emb_dim)
146
+
147
+ if self.parallel_mode:
148
+ for sbm_pair in self.seqband:
149
+ # z: (batch, n_bands, n_time, emb_dim)
150
+ sbm_t, sbm_f = sbm_pair[0], sbm_pair[1]
151
+ zt = sbm_t(z) # (batch, n_bands, n_time, emb_dim)
152
+ zf = sbm_f(z.transpose(1, 2)) # (batch, n_time, n_bands, emb_dim)
153
+ z = zt + zf.transpose(1, 2)
154
+ else:
155
+ for sbm in self.seqband:
156
+ z = sbm(z)
157
+ z = z.transpose(1, 2)
158
+
159
+ # (batch, n_bands, n_time, emb_dim)
160
+ # --> (batch, n_time, n_bands, emb_dim)
161
+ # OR
162
+ # (batch, n_time, n_bands, emb_dim)
163
+ # --> (batch, n_bands, n_time, emb_dim)
164
+
165
+ q = z
166
+ return q # (batch, n_bands, n_time, emb_dim)
core/models/e2e/bandit/utils.py ADDED
@@ -0,0 +1,583 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from abc import abstractmethod
3
+ from typing import Any, Callable
4
+
5
+ import numpy as np
6
+ import torch
7
+ from librosa import hz_to_midi, midi_to_hz
8
+ from torch import Tensor
9
+ from torchaudio import functional as taF
10
+ # from spafe.fbanks import bark_fbanks
11
+ # from spafe.utils.converters import erb2hz, hz2bark, hz2erb
12
+ from torchaudio.functional.functional import _create_triangular_filterbank
13
+
14
+
15
+ def band_widths_from_specs(band_specs):
16
+ return [e - i for i, e in band_specs]
17
+
18
+
19
+ def check_nonzero_bandwidth(band_specs):
20
+ # pprint(band_specs)
21
+ for fstart, fend in band_specs:
22
+ if fend - fstart <= 0:
23
+ raise ValueError("Bands cannot be zero-width")
24
+
25
+
26
+ def check_no_overlap(band_specs):
27
+ fend_prev = -1
28
+ for fstart_curr, fend_curr in band_specs:
29
+ if fstart_curr <= fend_prev:
30
+ raise ValueError("Bands cannot overlap")
31
+
32
+
33
+ def check_no_gap(band_specs):
34
+ fstart, _ = band_specs[0]
35
+ assert fstart == 0
36
+
37
+ fend_prev = -1
38
+ for fstart_curr, fend_curr in band_specs:
39
+ if fstart_curr - fend_prev > 1:
40
+ raise ValueError("Bands cannot leave gap")
41
+ fend_prev = fend_curr
42
+
43
+
44
+ class BandsplitSpecification:
45
+ def __init__(self, nfft: int, fs: int) -> None:
46
+ self.fs = fs
47
+ self.nfft = nfft
48
+ self.nyquist = fs / 2
49
+ self.max_index = nfft // 2 + 1
50
+
51
+ self.split500 = self.hertz_to_index(500)
52
+ self.split1k = self.hertz_to_index(1000)
53
+ self.split2k = self.hertz_to_index(2000)
54
+ self.split4k = self.hertz_to_index(4000)
55
+ self.split8k = self.hertz_to_index(8000)
56
+ self.split16k = self.hertz_to_index(16000)
57
+ self.split20k = self.hertz_to_index(20000)
58
+
59
+ self.above20k = [(self.split20k, self.max_index)]
60
+ self.above16k = [(self.split16k, self.split20k)] + self.above20k
61
+
62
+ def index_to_hertz(self, index: int):
63
+ return index * self.fs / self.nfft
64
+
65
+ def hertz_to_index(self, hz: float, round: bool = True):
66
+ index = hz * self.nfft / self.fs
67
+
68
+ if round:
69
+ index = int(np.round(index))
70
+
71
+ return index
72
+
73
+ def get_band_specs_with_bandwidth(
74
+ self,
75
+ start_index,
76
+ end_index,
77
+ bandwidth_hz
78
+ ):
79
+ band_specs = []
80
+ lower = start_index
81
+
82
+ while lower < end_index:
83
+ upper = int(np.floor(lower + self.hertz_to_index(bandwidth_hz)))
84
+ upper = min(upper, end_index)
85
+
86
+ band_specs.append((lower, upper))
87
+ lower = upper
88
+
89
+ return band_specs
90
+
91
+ @abstractmethod
92
+ def get_band_specs(self):
93
+ raise NotImplementedError
94
+
95
+
96
+ class VocalBandsplitSpecification(BandsplitSpecification):
97
+ def __init__(self, nfft: int, fs: int, version: str = "7") -> None:
98
+ super().__init__(nfft=nfft, fs=fs)
99
+
100
+ self.version = version
101
+
102
+ def get_band_specs(self):
103
+ return getattr(self, f"version{self.version}")()
104
+
105
+ @property
106
+ def version1(self):
107
+ return self.get_band_specs_with_bandwidth(
108
+ start_index=0, end_index=self.max_index, bandwidth_hz=1000
109
+ )
110
+
111
+ def version2(self):
112
+ below16k = self.get_band_specs_with_bandwidth(
113
+ start_index=0, end_index=self.split16k, bandwidth_hz=1000
114
+ )
115
+ below20k = self.get_band_specs_with_bandwidth(
116
+ start_index=self.split16k,
117
+ end_index=self.split20k,
118
+ bandwidth_hz=2000
119
+ )
120
+
121
+ return below16k + below20k + self.above20k
122
+
123
+ def version3(self):
124
+ below8k = self.get_band_specs_with_bandwidth(
125
+ start_index=0, end_index=self.split8k, bandwidth_hz=1000
126
+ )
127
+ below16k = self.get_band_specs_with_bandwidth(
128
+ start_index=self.split8k,
129
+ end_index=self.split16k,
130
+ bandwidth_hz=2000
131
+ )
132
+
133
+ return below8k + below16k + self.above16k
134
+
135
+ def version4(self):
136
+ below1k = self.get_band_specs_with_bandwidth(
137
+ start_index=0, end_index=self.split1k, bandwidth_hz=100
138
+ )
139
+ below8k = self.get_band_specs_with_bandwidth(
140
+ start_index=self.split1k,
141
+ end_index=self.split8k,
142
+ bandwidth_hz=1000
143
+ )
144
+ below16k = self.get_band_specs_with_bandwidth(
145
+ start_index=self.split8k,
146
+ end_index=self.split16k,
147
+ bandwidth_hz=2000
148
+ )
149
+
150
+ return below1k + below8k + below16k + self.above16k
151
+
152
+ def version5(self):
153
+ below1k = self.get_band_specs_with_bandwidth(
154
+ start_index=0, end_index=self.split1k, bandwidth_hz=100
155
+ )
156
+ below16k = self.get_band_specs_with_bandwidth(
157
+ start_index=self.split1k,
158
+ end_index=self.split16k,
159
+ bandwidth_hz=1000
160
+ )
161
+ below20k = self.get_band_specs_with_bandwidth(
162
+ start_index=self.split16k,
163
+ end_index=self.split20k,
164
+ bandwidth_hz=2000
165
+ )
166
+ return below1k + below16k + below20k + self.above20k
167
+
168
+ def version6(self):
169
+ below1k = self.get_band_specs_with_bandwidth(
170
+ start_index=0, end_index=self.split1k, bandwidth_hz=100
171
+ )
172
+ below4k = self.get_band_specs_with_bandwidth(
173
+ start_index=self.split1k,
174
+ end_index=self.split4k,
175
+ bandwidth_hz=500
176
+ )
177
+ below8k = self.get_band_specs_with_bandwidth(
178
+ start_index=self.split4k,
179
+ end_index=self.split8k,
180
+ bandwidth_hz=1000
181
+ )
182
+ below16k = self.get_band_specs_with_bandwidth(
183
+ start_index=self.split8k,
184
+ end_index=self.split16k,
185
+ bandwidth_hz=2000
186
+ )
187
+ return below1k + below4k + below8k + below16k + self.above16k
188
+
189
+ def version7(self):
190
+ below1k = self.get_band_specs_with_bandwidth(
191
+ start_index=0, end_index=self.split1k, bandwidth_hz=100
192
+ )
193
+ below4k = self.get_band_specs_with_bandwidth(
194
+ start_index=self.split1k,
195
+ end_index=self.split4k,
196
+ bandwidth_hz=250
197
+ )
198
+ below8k = self.get_band_specs_with_bandwidth(
199
+ start_index=self.split4k,
200
+ end_index=self.split8k,
201
+ bandwidth_hz=500
202
+ )
203
+ below16k = self.get_band_specs_with_bandwidth(
204
+ start_index=self.split8k,
205
+ end_index=self.split16k,
206
+ bandwidth_hz=1000
207
+ )
208
+ below20k = self.get_band_specs_with_bandwidth(
209
+ start_index=self.split16k,
210
+ end_index=self.split20k,
211
+ bandwidth_hz=2000
212
+ )
213
+ return below1k + below4k + below8k + below16k + below20k + self.above20k
214
+
215
+
216
+ class OtherBandsplitSpecification(VocalBandsplitSpecification):
217
+ def __init__(self, nfft: int, fs: int) -> None:
218
+ super().__init__(nfft=nfft, fs=fs, version="7")
219
+
220
+
221
+ class BassBandsplitSpecification(BandsplitSpecification):
222
+ def __init__(self, nfft: int, fs: int, version: str = "7") -> None:
223
+ super().__init__(nfft=nfft, fs=fs)
224
+
225
+ def get_band_specs(self):
226
+ below500 = self.get_band_specs_with_bandwidth(
227
+ start_index=0, end_index=self.split500, bandwidth_hz=50
228
+ )
229
+ below1k = self.get_band_specs_with_bandwidth(
230
+ start_index=self.split500,
231
+ end_index=self.split1k,
232
+ bandwidth_hz=100
233
+ )
234
+ below4k = self.get_band_specs_with_bandwidth(
235
+ start_index=self.split1k,
236
+ end_index=self.split4k,
237
+ bandwidth_hz=500
238
+ )
239
+ below8k = self.get_band_specs_with_bandwidth(
240
+ start_index=self.split4k,
241
+ end_index=self.split8k,
242
+ bandwidth_hz=1000
243
+ )
244
+ below16k = self.get_band_specs_with_bandwidth(
245
+ start_index=self.split8k,
246
+ end_index=self.split16k,
247
+ bandwidth_hz=2000
248
+ )
249
+ above16k = [(self.split16k, self.max_index)]
250
+
251
+ return below500 + below1k + below4k + below8k + below16k + above16k
252
+
253
+
254
+ class DrumBandsplitSpecification(BandsplitSpecification):
255
+ def __init__(self, nfft: int, fs: int) -> None:
256
+ super().__init__(nfft=nfft, fs=fs)
257
+
258
+ def get_band_specs(self):
259
+ below1k = self.get_band_specs_with_bandwidth(
260
+ start_index=0, end_index=self.split1k, bandwidth_hz=50
261
+ )
262
+ below2k = self.get_band_specs_with_bandwidth(
263
+ start_index=self.split1k,
264
+ end_index=self.split2k,
265
+ bandwidth_hz=100
266
+ )
267
+ below4k = self.get_band_specs_with_bandwidth(
268
+ start_index=self.split2k,
269
+ end_index=self.split4k,
270
+ bandwidth_hz=250
271
+ )
272
+ below8k = self.get_band_specs_with_bandwidth(
273
+ start_index=self.split4k,
274
+ end_index=self.split8k,
275
+ bandwidth_hz=500
276
+ )
277
+ below16k = self.get_band_specs_with_bandwidth(
278
+ start_index=self.split8k,
279
+ end_index=self.split16k,
280
+ bandwidth_hz=1000
281
+ )
282
+ above16k = [(self.split16k, self.max_index)]
283
+
284
+ return below1k + below2k + below4k + below8k + below16k + above16k
285
+
286
+
287
+
288
+
289
+ class PerceptualBandsplitSpecification(BandsplitSpecification):
290
+ def __init__(
291
+ self,
292
+ nfft: int,
293
+ fs: int,
294
+ fbank_fn: Callable[[int, int, float, float, int], torch.Tensor],
295
+ n_bands: int,
296
+ f_min: float = 0.0,
297
+ f_max: float = None
298
+ ) -> None:
299
+ super().__init__(nfft=nfft, fs=fs)
300
+ self.n_bands = n_bands
301
+ if f_max is None:
302
+ f_max = fs / 2
303
+
304
+ self.filterbank = fbank_fn(
305
+ n_bands, fs, f_min, f_max, self.max_index
306
+ )
307
+
308
+ weight_per_bin = torch.sum(
309
+ self.filterbank,
310
+ dim=0,
311
+ keepdim=True
312
+ ) # (1, n_freqs)
313
+ normalized_mel_fb = self.filterbank / weight_per_bin # (n_mels, n_freqs)
314
+
315
+ freq_weights = []
316
+ band_specs = []
317
+ for i in range(self.n_bands):
318
+ active_bins = torch.nonzero(self.filterbank[i, :]).squeeze().tolist()
319
+ if isinstance(active_bins, int):
320
+ active_bins = (active_bins, active_bins)
321
+ if len(active_bins) == 0:
322
+ continue
323
+ start_index = active_bins[0]
324
+ end_index = active_bins[-1] + 1
325
+ band_specs.append((start_index, end_index))
326
+ freq_weights.append(normalized_mel_fb[i, start_index:end_index])
327
+
328
+ self.freq_weights = freq_weights
329
+ self.band_specs = band_specs
330
+
331
+ def get_band_specs(self):
332
+ return self.band_specs
333
+
334
+ def get_freq_weights(self):
335
+ return self.freq_weights
336
+
337
+ def save_to_file(self, dir_path: str) -> None:
338
+
339
+ os.makedirs(dir_path, exist_ok=True)
340
+
341
+ import pickle
342
+
343
+ with open(os.path.join(dir_path, "mel_bandsplit_spec.pkl"), "wb") as f:
344
+ pickle.dump(
345
+ {
346
+ "band_specs": self.band_specs,
347
+ "freq_weights": self.freq_weights,
348
+ "filterbank": self.filterbank,
349
+ },
350
+ f,
351
+ )
352
+
353
+ def mel_filterbank(n_bands, fs, f_min, f_max, n_freqs):
354
+ fb = taF.melscale_fbanks(
355
+ n_mels=n_bands,
356
+ sample_rate=fs,
357
+ f_min=f_min,
358
+ f_max=f_max,
359
+ n_freqs=n_freqs,
360
+ ).T
361
+
362
+ fb[0, 0] = 1.0
363
+
364
+ return fb
365
+
366
+
367
+ class MelBandsplitSpecification(PerceptualBandsplitSpecification):
368
+ def __init__(
369
+ self,
370
+ nfft: int,
371
+ fs: int,
372
+ n_bands: int,
373
+ f_min: float = 0.0,
374
+ f_max: float = None
375
+ ) -> None:
376
+ super().__init__(fbank_fn=mel_filterbank, nfft=nfft, fs=fs, n_bands=n_bands, f_min=f_min, f_max=f_max)
377
+
378
+ def musical_filterbank(n_bands, fs, f_min, f_max, n_freqs,
379
+ scale="constant"):
380
+
381
+ nfft = 2 * (n_freqs - 1)
382
+ df = fs / nfft
383
+ # init freqs
384
+ f_max = f_max or fs / 2
385
+ f_min = f_min or 0
386
+ f_min = fs / nfft
387
+
388
+ n_octaves = np.log2(f_max / f_min)
389
+ n_octaves_per_band = n_octaves / n_bands
390
+ bandwidth_mult = np.power(2.0, n_octaves_per_band)
391
+
392
+ low_midi = max(0, hz_to_midi(f_min))
393
+ high_midi = hz_to_midi(f_max)
394
+ midi_points = np.linspace(low_midi, high_midi, n_bands)
395
+ hz_pts = midi_to_hz(midi_points)
396
+
397
+ low_pts = hz_pts / bandwidth_mult
398
+ high_pts = hz_pts * bandwidth_mult
399
+
400
+ low_bins = np.floor(low_pts / df).astype(int)
401
+ high_bins = np.ceil(high_pts / df).astype(int)
402
+
403
+ fb = np.zeros((n_bands, n_freqs))
404
+
405
+ for i in range(n_bands):
406
+ fb[i, low_bins[i]:high_bins[i]+1] = 1.0
407
+
408
+ fb[0, :low_bins[0]] = 1.0
409
+ fb[-1, high_bins[-1]+1:] = 1.0
410
+
411
+ return torch.as_tensor(fb)
412
+
413
+ class MusicalBandsplitSpecification(PerceptualBandsplitSpecification):
414
+ def __init__(
415
+ self,
416
+ nfft: int,
417
+ fs: int,
418
+ n_bands: int,
419
+ f_min: float = 0.0,
420
+ f_max: float = None
421
+ ) -> None:
422
+ super().__init__(fbank_fn=musical_filterbank, nfft=nfft, fs=fs, n_bands=n_bands, f_min=f_min, f_max=f_max)
423
+
424
+
425
+ # def bark_filterbank(
426
+ # n_bands, fs, f_min, f_max, n_freqs
427
+ # ):
428
+ # nfft = 2 * (n_freqs -1)
429
+ # fb, _ = bark_fbanks.bark_filter_banks(
430
+ # nfilts=n_bands,
431
+ # nfft=nfft,
432
+ # fs=fs,
433
+ # low_freq=f_min,
434
+ # high_freq=f_max,
435
+ # scale="constant"
436
+ # )
437
+
438
+ # return torch.as_tensor(fb)
439
+
440
+ # class BarkBandsplitSpecification(PerceptualBandsplitSpecification):
441
+ # def __init__(
442
+ # self,
443
+ # nfft: int,
444
+ # fs: int,
445
+ # n_bands: int,
446
+ # f_min: float = 0.0,
447
+ # f_max: float = None
448
+ # ) -> None:
449
+ # super().__init__(fbank_fn=bark_filterbank, nfft=nfft, fs=fs, n_bands=n_bands, f_min=f_min, f_max=f_max)
450
+
451
+
452
+ # def triangular_bark_filterbank(
453
+ # n_bands, fs, f_min, f_max, n_freqs
454
+ # ):
455
+
456
+ # all_freqs = torch.linspace(0, fs // 2, n_freqs)
457
+
458
+ # # calculate mel freq bins
459
+ # m_min = hz2bark(f_min)
460
+ # m_max = hz2bark(f_max)
461
+
462
+ # m_pts = torch.linspace(m_min, m_max, n_bands + 2)
463
+ # f_pts = 600 * torch.sinh(m_pts / 6)
464
+
465
+ # # create filterbank
466
+ # fb = _create_triangular_filterbank(all_freqs, f_pts)
467
+
468
+ # fb = fb.T
469
+
470
+ # first_active_band = torch.nonzero(torch.sum(fb, dim=-1))[0, 0]
471
+ # first_active_bin = torch.nonzero(fb[first_active_band, :])[0, 0]
472
+
473
+ # fb[first_active_band, :first_active_bin] = 1.0
474
+
475
+ # return fb
476
+
477
+ # class TriangularBarkBandsplitSpecification(PerceptualBandsplitSpecification):
478
+ # def __init__(
479
+ # self,
480
+ # nfft: int,
481
+ # fs: int,
482
+ # n_bands: int,
483
+ # f_min: float = 0.0,
484
+ # f_max: float = None
485
+ # ) -> None:
486
+ # super().__init__(fbank_fn=triangular_bark_filterbank, nfft=nfft, fs=fs, n_bands=n_bands, f_min=f_min, f_max=f_max)
487
+
488
+
489
+
490
+ # def minibark_filterbank(
491
+ # n_bands, fs, f_min, f_max, n_freqs
492
+ # ):
493
+ # fb = bark_filterbank(
494
+ # n_bands,
495
+ # fs,
496
+ # f_min,
497
+ # f_max,
498
+ # n_freqs
499
+ # )
500
+
501
+ # fb[fb < np.sqrt(0.5)] = 0.0
502
+
503
+ # return fb
504
+
505
+ # class MiniBarkBandsplitSpecification(PerceptualBandsplitSpecification):
506
+ # def __init__(
507
+ # self,
508
+ # nfft: int,
509
+ # fs: int,
510
+ # n_bands: int,
511
+ # f_min: float = 0.0,
512
+ # f_max: float = None
513
+ # ) -> None:
514
+ # super().__init__(fbank_fn=minibark_filterbank, nfft=nfft, fs=fs, n_bands=n_bands, f_min=f_min, f_max=f_max)
515
+
516
+
517
+
518
+
519
+
520
+ # def erb_filterbank(
521
+ # n_bands: int,
522
+ # fs: int,
523
+ # f_min: float,
524
+ # f_max: float,
525
+ # n_freqs: int,
526
+ # ) -> Tensor:
527
+ # # freq bins
528
+ # A = (1000 * np.log(10)) / (24.7 * 4.37)
529
+ # all_freqs = torch.linspace(0, fs // 2, n_freqs)
530
+
531
+ # # calculate mel freq bins
532
+ # m_min = hz2erb(f_min)
533
+ # m_max = hz2erb(f_max)
534
+
535
+ # m_pts = torch.linspace(m_min, m_max, n_bands + 2)
536
+ # f_pts = (torch.pow(10, (m_pts / A)) - 1)/ 0.00437
537
+
538
+ # # create filterbank
539
+ # fb = _create_triangular_filterbank(all_freqs, f_pts)
540
+
541
+ # fb = fb.T
542
+
543
+
544
+ # first_active_band = torch.nonzero(torch.sum(fb, dim=-1))[0, 0]
545
+ # first_active_bin = torch.nonzero(fb[first_active_band, :])[0, 0]
546
+
547
+ # fb[first_active_band, :first_active_bin] = 1.0
548
+
549
+ # return fb
550
+
551
+
552
+
553
+ # class EquivalentRectangularBandsplitSpecification(PerceptualBandsplitSpecification):
554
+ # def __init__(
555
+ # self,
556
+ # nfft: int,
557
+ # fs: int,
558
+ # n_bands: int,
559
+ # f_min: float = 0.0,
560
+ # f_max: float = None
561
+ # ) -> None:
562
+ # super().__init__(fbank_fn=erb_filterbank, nfft=nfft, fs=fs, n_bands=n_bands, f_min=f_min, f_max=f_max)
563
+
564
+ if __name__ == "__main__":
565
+ import pandas as pd
566
+
567
+ band_defs = []
568
+
569
+ for bands in [VocalBandsplitSpecification]:
570
+ band_name = bands.__name__.replace("BandsplitSpecification", "")
571
+
572
+ mbs = bands(nfft=2048, fs=44100).get_band_specs()
573
+
574
+ for i, (f_min, f_max) in enumerate(mbs):
575
+ band_defs.append({
576
+ "band": band_name,
577
+ "band_index": i,
578
+ "f_min": f_min,
579
+ "f_max": f_max
580
+ })
581
+
582
+ df = pd.DataFrame(band_defs)
583
+ df.to_csv("vox7bands.csv", index=False)
core/models/e2e/base.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import warnings
2
+ from typing import Any, Dict, Optional, Tuple, Union
3
+ import pytorch_lightning as pl
4
+
5
+ #from audiocraft.models import encodec
6
+ import torch
7
+ from torch import nn
8
+ from torch.nn import functional as F
9
+
10
+ from ...types import (
11
+ BatchedInputOutput,
12
+ InputType,
13
+ OperationMode,
14
+ OutputType,
15
+ SimpleishNamespace,
16
+ TensorCollection
17
+ )
18
+
19
+ import torchaudio as ta
20
+
21
+
22
+ class BaseEndToEndModule(pl.LightningModule):
23
+
24
+ def __init__(
25
+ self,
26
+ ) -> None:
27
+ super().__init__()
28
+
29
+
30
+ if __name__ == "__main__":
31
+ model = BaseEndToEndModule()
32
+ print(model)
33
+ print(model.__class__.__name__)
34
+ print(model.__module__)
core/models/e2e/conditioners/__init__.py ADDED
File without changes
core/models/e2e/conditioners/base.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from torch import nn
2
+
3
+
4
+ class Conditioning(nn.Module):
5
+ def __init__(
6
+ self, cond_embedding_dim: int, channels: int, channels_per_group: int = 16
7
+ ):
8
+ super().__init__()
9
+
10
+ self.channels = channels
11
+ self.cond_embedding_dim = cond_embedding_dim
12
+ self.channels_per_group = channels_per_group
13
+
14
+ self.gn = nn.GroupNorm(self.channels // self.channels_per_group, self.channels)
15
+
16
+ def forward(self, x, w):
17
+ raise NotImplementedError
18
+
19
+
20
+ class PassThroughConditioning(Conditioning):
21
+ def __init__(
22
+ self, cond_embedding_dim: int, channels: int, channels_per_group: int = 16
23
+ ):
24
+ super().__init__(cond_embedding_dim, channels, channels_per_group)
25
+
26
+ def forward(self, x, w):
27
+ return self.gn(x)
core/models/e2e/conditioners/film.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ from core.models.e2e.conditioners.base import Conditioning
4
+
5
+
6
+ from torch import nn
7
+ from torch.nn.modules import activation as activation_
8
+
9
+
10
+ class FiLM(Conditioning):
11
+ def __init__(
12
+ self,
13
+ cond_embedding_dim: int,
14
+ channels: int,
15
+ additive: bool = True,
16
+ multiplicative: bool = False,
17
+ depth: int = 1,
18
+ activation: str = "ELU",
19
+ channels_per_group: int = 16,
20
+ ):
21
+ super().__init__(
22
+ channels=channels,
23
+ channels_per_group=channels_per_group,
24
+ cond_embedding_dim=cond_embedding_dim,
25
+ )
26
+
27
+ self.additive = additive
28
+ self.multiplicative = multiplicative
29
+ self.depth = depth
30
+ self.activation = activation
31
+
32
+ Activation = activation_.__dict__[activation]
33
+
34
+ if self.multiplicative:
35
+
36
+ if depth == 1:
37
+ self.gamma = nn.Linear(self.cond_embedding_dim, self.channels)
38
+ else:
39
+ layers = [nn.Linear(self.cond_embedding_dim, self.channels)]
40
+ for _ in range(depth - 1):
41
+ layers += [Activation(), nn.Linear(self.channels, self.channels)]
42
+ self.gamma = nn.Sequential(*layers)
43
+ else:
44
+ self.gamma = None
45
+
46
+ if self.additive:
47
+ if depth == 1:
48
+ self.beta = nn.Linear(self.cond_embedding_dim, self.channels)
49
+ else:
50
+ layers = [nn.Linear(self.cond_embedding_dim, self.channels)]
51
+ for _ in range(depth - 1):
52
+ layers += [Activation(), nn.Linear(self.channels, self.channels)]
53
+ self.beta = nn.Sequential(*layers)
54
+ else:
55
+ self.beta = None
56
+
57
+ def forward(self, x, w):
58
+
59
+ x = self.gn(x)
60
+
61
+ if self.multiplicative:
62
+ gamma = self.gamma(w)
63
+
64
+ if len(x.shape) == 4:
65
+ gamma = gamma[:, :, None, None]
66
+ elif len(x.shape) == 3:
67
+ gamma = gamma[:, :, None]
68
+ elif len(x.shape) == 2:
69
+ pass
70
+ else:
71
+ raise ValueError(f"Invalid shape for input tensor: {x.shape}")
72
+
73
+ x = gamma * x
74
+
75
+ if self.additive:
76
+ beta = self.beta(w)
77
+ if len(x.shape) == 4:
78
+ beta = beta[:, :, None, None]
79
+ elif len(x.shape) == 3:
80
+ beta = beta[:, :, None]
81
+ elif len(x.shape) == 2:
82
+ pass
83
+ else:
84
+ raise ValueError(f"Invalid shape for input tensor: {x.shape}")
85
+
86
+ x = x + beta
87
+
88
+ return x
89
+
90
+ class CosineSimiliarity(Conditioning):
91
+ def __init__(self, cond_embedding_dim: int, channels: int, channels_per_group: int = 16):
92
+ super().__init__(cond_embedding_dim, channels, channels_per_group)
93
+
94
+ self.csim = nn.CosineSimilarity(dim=1)
95
+ self.proj = nn.Linear(self.cond_embedding_dim, self.channels * self.channels)
96
+
97
+ def forward(self, x, w):
98
+
99
+
100
+ x = self.gn(x)
101
+
102
+ gamma = self.gamma(w)
103
+
104
+ if len(x.shape) == 4:
105
+ gamma = gamma[:, :, None, None]
106
+ elif len(x.shape) == 3:
107
+ gamma = gamma[:, :, None]
108
+ elif len(x.shape) == 2:
109
+ pass
110
+ else:
111
+ raise ValueError(f"Invalid shape for input tensor: {x.shape}")
112
+
113
+ c = self.csim(gamma, x)
114
+
115
+ x = c[:, None, ...] * x
116
+
117
+
118
+
119
+
120
+
121
+ class GeneralizedBilinear(nn.Bilinear):
122
+ def __init__(self, in1_features: int, in2_features: int, out_features: int, bias: bool = True, device=None, dtype=None) -> None:
123
+ super().__init__(in1_features, in2_features, out_features, bias, device, dtype)
124
+
125
+ def forward(self, x1, x2):
126
+
127
+ out = torch.einsum(
128
+ "bc...,acd,bd->ba...", x1, self.weight, x2
129
+ )
130
+
131
+ if self.bias is not None:
132
+ ndim = out.ndim
133
+ bias = torch.reshape(self.bias, (1, -1) + (1,) * (ndim - 2))
134
+
135
+ out = out + bias
136
+
137
+ return out
138
+
139
+
140
+ class BilinearFiLM(Conditioning):
141
+ def __init__(
142
+ self,
143
+ cond_embedding_dim: int,
144
+ channels: int,
145
+ additive: bool = True,
146
+ multiplicative: bool = False,
147
+ depth: int = 2,
148
+ activation: str = "ELU",
149
+ channels_per_group: int = 16,
150
+ ):
151
+ super().__init__(
152
+ channels=channels,
153
+ channels_per_group=channels_per_group,
154
+ cond_embedding_dim=cond_embedding_dim,
155
+ )
156
+
157
+ self.additive = additive
158
+ self.multiplicative = multiplicative
159
+ self.depth = depth
160
+ assert depth == 2, "Only depth 2 is supported for BilinearFiLM"
161
+ self.activation = activation
162
+
163
+ Activation = activation_.__dict__[activation]
164
+
165
+ if self.multiplicative:
166
+ self.gamma_proj = nn.Sequential(
167
+ nn.Linear(self.cond_embedding_dim, self.channels),
168
+ Activation(),
169
+ )
170
+ self.gamma_bilinear = GeneralizedBilinear(self.channels, self.channels, self.channels)
171
+ else:
172
+ self.gamma = None
173
+
174
+ if self.additive:
175
+ self.beta_proj = nn.Sequential(
176
+ nn.Linear(self.cond_embedding_dim, self.channels),
177
+ Activation(),
178
+ )
179
+ self.beta_bilinear = GeneralizedBilinear(self.channels, self.channels, self.channels)
180
+ else:
181
+ self.beta = None
182
+
183
+ def forward(self, x, w):
184
+
185
+ x = self.gn(x)
186
+
187
+ if self.multiplicative:
188
+ gamma = self.gamma_proj(w)
189
+ gamma = self.gamma_bilinear(x, gamma)
190
+ x = gamma * x
191
+
192
+ if self.additive:
193
+ beta = self.beta_proj(w)
194
+ beta = self.beta_bilinear(x, beta)
195
+ x = x + beta
196
+
197
+ return x
core/models/e2e/querier/__init__.py ADDED
File without changes
core/models/e2e/querier/passt.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torchaudio as ta
3
+ from hear21passt.base import get_basic_model
4
+ from torch import nn
5
+
6
+ class Passt(nn.Module):
7
+
8
+ PASST_EMB_DIM: int = 768
9
+ PASST_FS: int = 32000
10
+
11
+ def __init__(
12
+ self,
13
+ original_fs: int=44100,
14
+ passt_fs: int=PASST_FS,
15
+ ):
16
+ super().__init__()
17
+
18
+ self.passt = get_basic_model(mode="embed_only", arch="openmic").eval()
19
+ self.resample = ta.transforms.Resample(
20
+ orig_freq=original_fs, new_freq=passt_fs
21
+ ).eval()
22
+
23
+ for p in self.passt.parameters():
24
+ p.requires_grad = False
25
+
26
+ def forward(self, x):
27
+ """
28
+ Forward pass of the PasstWrapper model.
29
+
30
+ Args:
31
+ qspec (torch.Tensor): Query spectrogram.
32
+ qaudio (torch.Tensor): Query audio.
33
+
34
+ Returns:
35
+ torch.Tensor: Embedding output.
36
+ """
37
+ with torch.no_grad():
38
+ x = torch.mean(x, dim=1)
39
+ x = self.resample(x)
40
+
41
+ specs = self.passt.mel(x)[..., :998]
42
+ specs = specs[:, None, ...]
43
+ _, z = self.passt.net(specs)
44
+
45
+ return z
46
+
47
+
48
+ class PasstWrapper(nn.Module):
49
+
50
+ PASST_EMB_DIM: int = 768
51
+ PASST_FS: int = 32000
52
+
53
+ def __init__(
54
+ self,
55
+ cond_emb_dim: int = 384,
56
+ original_cond_emb_dim=PASST_EMB_DIM,
57
+ original_fs: int=44100,
58
+ passt_fs: int=PASST_FS,
59
+ ):
60
+ super().__init__()
61
+ self.cond_emb_dim = cond_emb_dim
62
+
63
+ self.passt = get_basic_model(mode="embed_only", arch="openmic").eval()
64
+ self.proj = nn.Linear(original_cond_emb_dim, cond_emb_dim) if cond_emb_dim is not None else nn.Identity()
65
+ self.resample = ta.transforms.Resample(
66
+ orig_freq=original_fs, new_freq=passt_fs
67
+ ).eval()
68
+
69
+ for p in self.passt.parameters():
70
+ p.requires_grad = False
71
+
72
+ def forward(self, qspec, qaudio):
73
+ """
74
+ Forward pass of the PasstWrapper model.
75
+
76
+ Args:
77
+ qspec (torch.Tensor): Query spectrogram.
78
+ qaudio (torch.Tensor): Query audio.
79
+
80
+ Returns:
81
+ torch.Tensor: Embedding output.
82
+ """
83
+ with torch.no_grad():
84
+ x = torch.mean(qaudio, dim=1)
85
+ x = self.resample(x)
86
+
87
+ specs = self.passt.mel(x)[..., :998]
88
+ specs = specs[:, None, ...]
89
+ _, z = self.passt.net(specs)
90
+
91
+ z = self.proj(z)
92
+
93
+ return z
core/models/ebase.py ADDED
@@ -0,0 +1,506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import os.path
3
+ from collections import defaultdict
4
+ from itertools import chain, combinations
5
+ from pprint import pprint
6
+ from typing import Any, Dict, Iterator, Mapping, Optional, Tuple, Type, TypedDict
7
+
8
+ import pytorch_lightning as pl
9
+ import torch
10
+ import torchaudio as ta
11
+ import torchmetrics as tm
12
+ from torch import nn, optim
13
+ from torch.optim import lr_scheduler
14
+ from torch.optim.lr_scheduler import LRScheduler
15
+ from tqdm import tqdm
16
+
17
+ from torch.nn import functional as F
18
+
19
+ from core.types import BatchedInputOutput, OperationMode, RawInputType, SimpleishNamespace
20
+ from core.types import (
21
+ InputType,
22
+ OutputType,
23
+ LossOutputType,
24
+ MetricOutputType,
25
+ ModelType,
26
+ OptimizerType,
27
+ SchedulerType,
28
+ MetricType,
29
+ LossType,
30
+ OptimizationBundle,
31
+ LossHandler,
32
+ MetricHandler,
33
+ AugmentationHandler,
34
+ InferenceHandler,
35
+ )
36
+
37
+
38
+ class EndToEndLightningSystem(pl.LightningModule):
39
+ def __init__(
40
+ self,
41
+ model: ModelType,
42
+ loss_handler: LossHandler,
43
+ metrics: MetricHandler,
44
+ augmentation_handler: AugmentationHandler,
45
+ inference_handler: InferenceHandler,
46
+ optimization_bundle: OptimizationBundle,
47
+ fast_run: bool = False,
48
+ commitment_weight: float = 1.0,
49
+ batch_size: Optional[int] = None,
50
+ effective_batch_size: Optional[int] = None,
51
+ ) -> None:
52
+ super().__init__()
53
+
54
+ self.model = model
55
+
56
+ self.loss = loss_handler
57
+
58
+ self.metrics = metrics
59
+ self.optimization = optimization_bundle
60
+ self.augmentation = augmentation_handler
61
+ self.inference = inference_handler
62
+
63
+ self.fast_run = fast_run
64
+
65
+ self.model.fast_run = fast_run
66
+
67
+ self.commitment_weight = commitment_weight
68
+
69
+ self.batch_size = batch_size
70
+ self.effective_batch_size = effective_batch_size if effective_batch_size is not None else batch_size
71
+ self.accum_ratio = self.effective_batch_size // self.batch_size if self.effective_batch_size is not None else 1
72
+
73
+ self.output_dir = None
74
+ self.split_size = None
75
+
76
+ def configure_optimizers(self) -> Any:
77
+ optimizer = self.optimization.optimizer.cls(
78
+ self.model.parameters(),
79
+ **self.optimization.optimizer.kwargs
80
+ )
81
+
82
+ ret = {
83
+ "optimizer": optimizer,
84
+ }
85
+
86
+ if self.optimization.scheduler is not None:
87
+ scheduler = self.optimization.scheduler.cls(
88
+ optimizer,
89
+ **self.optimization.scheduler.kwargs
90
+ )
91
+ ret["lr_scheduler"] = scheduler
92
+
93
+ return ret
94
+
95
+ def compute_loss(
96
+ self,
97
+ batch: BatchedInputOutput,
98
+ mode=OperationMode.TRAIN
99
+ ) -> LossOutputType:
100
+ loss_dict = self.loss(batch)
101
+ return loss_dict
102
+
103
+ # TODO: move to a metric handler
104
+ def update_metrics(
105
+ self,
106
+ batch: BatchedInputOutput,
107
+ mode: OperationMode = OperationMode.TRAIN,
108
+ ) -> None:
109
+ metrics: MetricType = self.metrics.get_mode(mode)
110
+
111
+ for stem, metric in metrics.items():
112
+ if stem not in batch.estimates.keys():
113
+ continue
114
+ metric.update(batch)
115
+
116
+ # TODO: move to a metric handler
117
+ def compute_metrics(self, mode: OperationMode) -> MetricOutputType:
118
+ metrics: MetricType = self.metrics.get_mode(mode)
119
+
120
+ metric_dict = {}
121
+
122
+ for stem, metric in metrics.items():
123
+ md = metric.compute()
124
+ metric_dict.update({f"{stem}/{k}": v for k, v in md.items()})
125
+
126
+ self.log_dict(metric_dict, prog_bar=True, logger=False)
127
+
128
+ return metric_dict
129
+
130
+ # TODO: move to a metric handler
131
+ def reset_metrics(self, mode: OperationMode) -> None:
132
+ metrics: MetricType = self.metrics.get_mode(mode)
133
+
134
+ for _, metric in metrics.items():
135
+ metric.reset()
136
+
137
+ def forward(self, batch: RawInputType) -> Tuple[InputType, OutputType]:
138
+ batch = self.model(batch)
139
+ return batch
140
+
141
+ def common_step(
142
+ self, batch: RawInputType, mode: OperationMode, batch_idx: int = -1
143
+ ) -> Tuple[OutputType, LossOutputType]:
144
+ batch = BatchedInputOutput.from_dict(batch)
145
+ batch = self.forward(batch)
146
+
147
+ loss_dict = self.compute_loss(batch, mode=mode)
148
+
149
+ if not self.fast_run:
150
+ with torch.no_grad():
151
+ self.update_metrics(batch, mode=mode)
152
+
153
+ return loss_dict
154
+
155
+ def training_step(self, batch: RawInputType, batch_idx: int) -> LossOutputType:
156
+ # augmented_batch = self.augmentation(batch, mode=OperationMode.TRAIN)
157
+
158
+ self.model.train()
159
+
160
+ loss_dict = self.common_step(batch, mode=OperationMode.TRAIN, batch_idx=batch_idx)
161
+
162
+ self.log_dict_with_prefix(loss_dict, prefix=OperationMode.TRAIN, prog_bar=True)
163
+
164
+ return loss_dict
165
+
166
+ def on_train_batch_end(
167
+ self, outputs: OutputType, batch: RawInputType, batch_idx: int
168
+ ) -> None:
169
+
170
+ if self.fast_run:
171
+ return
172
+
173
+ if (batch_idx + 1) % self.accum_ratio == 0:
174
+ metric_dict = self.compute_metrics(mode=OperationMode.TRAIN)
175
+ self.log_dict_with_prefix(metric_dict, prefix=OperationMode.TRAIN)
176
+ self.reset_metrics(mode=OperationMode.TRAIN)
177
+
178
+ @torch.inference_mode()
179
+ def validation_step(
180
+ self, batch: RawInputType, batch_idx: int, dataloader_idx: int = 0
181
+ ) -> Dict[str, Any]:
182
+
183
+ self.model.eval()
184
+
185
+ with torch.inference_mode():
186
+ loss_dict = self.common_step(batch, mode=OperationMode.VAL)
187
+
188
+ self.log_dict_with_prefix(loss_dict, prefix=OperationMode.VAL)
189
+
190
+ return loss_dict
191
+
192
+ def on_validation_epoch_start(self) -> None:
193
+ self.reset_metrics(mode=OperationMode.VAL)
194
+
195
+ def on_validation_epoch_end(self) -> None:
196
+ if self.fast_run:
197
+ return
198
+
199
+ metric_dict = self.compute_metrics(mode=OperationMode.VAL)
200
+ self.log_dict_with_prefix(
201
+ metric_dict, OperationMode.VAL, prog_bar=True, add_dataloader_idx=False
202
+ )
203
+ self.reset_metrics(mode=OperationMode.VAL)
204
+
205
+
206
+ def save_to_audio(self, batch: BatchedInputOutput, batch_idx: int) -> None:
207
+
208
+ batch_size = batch["mixture"]["audio"].shape[0]
209
+
210
+ assert batch_size == 1, "Batch size must be 1 for inference"
211
+
212
+ metadata = batch.metadata
213
+
214
+ song_id = metadata["mix"][0]
215
+ stem = metadata["stem"][0]
216
+
217
+ log_dir = os.path.join(self.logger.log_dir, "audio")
218
+
219
+ os.makedirs(os.path.join(log_dir, song_id), exist_ok=True)
220
+
221
+ audio = batch.estimates[stem]["audio"]
222
+
223
+ audio = audio.squeeze(0).cpu().numpy()
224
+
225
+ audio_path = os.path.join(log_dir, song_id, f"{stem}.wav")
226
+
227
+ ta.save(audio_path, torch.tensor(audio), self.inference.fs)
228
+
229
+ def save_vdbo_to_audio(self, batch: BatchedInputOutput, batch_idx: int) -> None:
230
+
231
+ batch_size = batch["mixture"]["audio"].shape[0]
232
+
233
+ assert batch_size == 1, "Batch size must be 1 for inference"
234
+
235
+ metadata = batch.metadata
236
+
237
+ song_id = metadata["song_id"][0]
238
+
239
+ log_dir = os.path.join(self.logger.log_dir, "audio")
240
+
241
+ os.makedirs(os.path.join(log_dir, song_id), exist_ok=True)
242
+
243
+ for stem, audio in batch.estimates.items():
244
+ audio = audio["audio"]
245
+ audio = audio.squeeze(0).cpu().numpy()
246
+
247
+ audio_path = os.path.join(log_dir, song_id, f"{stem}.wav")
248
+
249
+ ta.save(audio_path, torch.tensor(audio), self.inference.fs)
250
+
251
+ @torch.inference_mode()
252
+ def chunked_inference(
253
+ self, batch: RawInputType, batch_idx: int = -1, dataloader_idx: int = 0
254
+ ) -> BatchedInputOutput:
255
+ batch = BatchedInputOutput.from_dict(batch)
256
+
257
+ audio = batch["mixture"]["audio"]
258
+
259
+ b, c, n_samples = audio.shape
260
+
261
+ assert b == 1
262
+
263
+ fs = self.inference.fs
264
+
265
+ chunk_size = int(self.inference.chunk_size_seconds * fs)
266
+ hop_size = int(self.inference.hop_size_seconds * fs)
267
+
268
+ batch_size = self.inference.batch_size
269
+
270
+ overlap = chunk_size - hop_size
271
+
272
+ scaler = chunk_size / (2 * hop_size)
273
+
274
+ n_chunks = int(math.ceil(
275
+ (n_samples + 4 * overlap - chunk_size) / hop_size
276
+ )) + 1
277
+
278
+ pad = (n_chunks - 1) * hop_size + chunk_size - n_samples
279
+
280
+ # print(audio.shape)
281
+ audio = F.pad(
282
+ audio,
283
+ pad=(2 * overlap, 2 * overlap + pad),
284
+ mode="reflect"
285
+ )
286
+ padded_length = audio.shape[-1]
287
+ audio = audio.reshape(c, 1, -1, 1)
288
+
289
+ chunked_audio = F.unfold(
290
+ audio,
291
+ kernel_size=(chunk_size, 1),
292
+ stride=(hop_size, 1)
293
+ ) # (c, chunk_size, n_chunk)
294
+
295
+ # print(chunked_audio.shape)
296
+
297
+ chunked_audio = chunked_audio.permute(2, 0, 1).reshape(-1, c, chunk_size)
298
+
299
+ n_chunks = chunked_audio.shape[0]
300
+
301
+ n_batch = math.ceil(n_chunks / batch_size)
302
+
303
+ outputs = []
304
+
305
+ for i in tqdm(range(n_batch)):
306
+ start = i * batch_size
307
+ end = min((i + 1) * batch_size, n_chunks)
308
+
309
+ chunked_batch = SimpleishNamespace(
310
+ mixture={
311
+ "audio": chunked_audio[start:end]
312
+ },
313
+ query=batch["query"],
314
+ estimates=batch["estimates"]
315
+ )
316
+
317
+ output = self.forward(chunked_batch)
318
+ outputs.append(output.estimates["target"]["audio"])
319
+
320
+ output = torch.cat(outputs, dim=0) # (n_chunks, c, chunk_size)
321
+ window = torch.hann_window(chunk_size, device=self.device).reshape(1, 1, chunk_size)
322
+ output = output * window / scaler
323
+
324
+ output = torch.permute(output, (1, 2, 0))
325
+
326
+ output = F.fold(
327
+ output,
328
+ output_size=(padded_length, 1),
329
+ kernel_size=(chunk_size, 1),
330
+ stride=(hop_size, 1)
331
+ ) # (c, 1, t, 1)
332
+
333
+ output = output[None, :, 0, 2*overlap: n_samples + 2*overlap, 0]
334
+
335
+ stem = batch.metadata["stem"][0]
336
+
337
+ batch["estimates"][stem] = {
338
+ "audio": output
339
+ }
340
+
341
+ return batch
342
+
343
+ def chunked_vdbo_inference(
344
+ self, batch: RawInputType, batch_idx: int = -1, dataloader_idx: int = 0
345
+ ) -> BatchedInputOutput:
346
+ batch = BatchedInputOutput.from_dict(batch)
347
+
348
+ audio = batch["mixture"]["audio"]
349
+
350
+ b, c, n_samples = audio.shape
351
+
352
+ assert b == 1
353
+
354
+ fs = self.inference.fs
355
+
356
+ chunk_size = int(self.inference.chunk_size_seconds * fs)
357
+ hop_size = int(self.inference.hop_size_seconds * fs)
358
+
359
+ batch_size = self.inference.batch_size
360
+
361
+ overlap = chunk_size - hop_size
362
+
363
+ scaler = chunk_size / (2 * hop_size)
364
+
365
+ n_chunks = int(math.ceil(
366
+ (n_samples + 4 * overlap - chunk_size) / hop_size
367
+ )) + 1
368
+
369
+ pad = (n_chunks - 1) * hop_size + chunk_size - n_samples
370
+
371
+ # print(audio.shape)
372
+ audio = F.pad(
373
+ audio,
374
+ pad=(2 * overlap, 2 * overlap + pad),
375
+ mode="reflect"
376
+ )
377
+ padded_length = audio.shape[-1]
378
+ audio = audio.reshape(c, 1, -1, 1)
379
+
380
+ chunked_audio = F.unfold(
381
+ audio,
382
+ kernel_size=(chunk_size, 1),
383
+ stride=(hop_size, 1)
384
+ ) # (c, chunk_size, n_chunk)
385
+
386
+ # print(chunked_audio.shape)
387
+
388
+ chunked_audio = chunked_audio.permute(2, 0, 1).reshape(-1, c, chunk_size)
389
+
390
+ n_chunks = chunked_audio.shape[0]
391
+
392
+ n_batch = math.ceil(n_chunks / batch_size)
393
+
394
+ outputs = defaultdict(list)
395
+
396
+ for i in tqdm(range(n_batch)):
397
+ start = i * batch_size
398
+ end = min((i + 1) * batch_size, n_chunks)
399
+
400
+ chunked_batch = SimpleishNamespace(
401
+ mixture={
402
+ "audio": chunked_audio[start:end]
403
+ },
404
+ estimates=batch["estimates"]
405
+ )
406
+
407
+ output = self.forward(chunked_batch)
408
+
409
+ for stem, estimate in output.estimates.items():
410
+ outputs[stem].append(estimate["audio"])
411
+
412
+ for stem, outputs_ in outputs.items():
413
+
414
+ output = torch.cat(outputs_, dim=0) # (n_chunks, c, chunk_size)
415
+ window = torch.hann_window(chunk_size, device=self.device).reshape(1, 1, chunk_size)
416
+ output = output * window / scaler
417
+
418
+ output = torch.permute(output, (1, 2, 0))
419
+
420
+ output = F.fold(
421
+ output,
422
+ output_size=(padded_length, 1),
423
+ kernel_size=(chunk_size, 1),
424
+ stride=(hop_size, 1)
425
+ ) # (c, 1, t, 1)
426
+
427
+ output = output[None, :, 0, 2*overlap: n_samples + 2*overlap, 0]
428
+
429
+ batch["estimates"][stem] = {
430
+ "audio": output
431
+ }
432
+
433
+ return batch
434
+
435
+
436
+ def on_test_epoch_start(self) -> None:
437
+ self.reset_metrics(mode=OperationMode.TEST)
438
+
439
+ def test_step(
440
+ self, batch: RawInputType, batch_idx: int, dataloader_idx: int = 0
441
+ ) -> Any:
442
+
443
+ self.model.eval()
444
+
445
+ if "query" in batch.keys():
446
+ batch = self.chunked_inference(batch, batch_idx, dataloader_idx)
447
+ else:
448
+ batch = self.chunked_vdbo_inference(batch, batch_idx, dataloader_idx)
449
+
450
+ self.reset_metrics(mode=OperationMode.TEST)
451
+ self.update_metrics(batch, mode=OperationMode.TEST)
452
+ metrics = self.compute_metrics(mode=OperationMode.TEST)
453
+ # metrics["song_id"] = batch.metadata["mix"][0]
454
+ self.log_dict_with_prefix(metrics, OperationMode.TEST,
455
+ on_step=True, on_epoch=False, prog_bar=True)
456
+ self.reset_metrics(mode=OperationMode.TEST)
457
+
458
+ # pprint(metrics)
459
+
460
+ return batch
461
+
462
+ def on_test_epoch_end(self) -> None:
463
+ self.reset_metrics(mode=OperationMode.TEST)
464
+
465
+ def set_output_path(self, output_dir: str) -> None:
466
+ self.output_dir = output_dir
467
+
468
+ def predict_step(
469
+ self, batch: RawInputType, batch_idx: int, dataloader_idx: int = 0
470
+ ) -> Any:
471
+
472
+ self.model.eval()
473
+
474
+ if "query" in batch.keys():
475
+ batch = self.chunked_inference(batch, batch_idx, dataloader_idx)
476
+
477
+ self.save_to_audio(batch, batch_idx)
478
+ else:
479
+ batch = self.chunked_vdbo_inference(batch, batch_idx, dataloader_idx)
480
+ self.save_vdbo_to_audio(batch, batch_idx)
481
+
482
+ def load_state_dict(
483
+ self, state_dict: Mapping[str, Any], strict: bool = False
484
+ ) -> Any:
485
+ return super().load_state_dict(state_dict, strict=False)
486
+
487
+ def log_dict_with_prefix(
488
+ self,
489
+ dict_: Dict[str, torch.Tensor],
490
+ prefix: str,
491
+ batch_size: Optional[int] = None,
492
+ **kwargs: Any,
493
+ ) -> None:
494
+
495
+
496
+ self.log_dict(
497
+ {f"{prefix}/{k}": v for k, v in dict_.items()},
498
+ batch_size=batch_size,
499
+ logger=True,
500
+ sync_dist=True,
501
+ **kwargs,
502
+ # on_step=True,
503
+ # on_epoch=False,
504
+ )
505
+
506
+ self.logger.save()
core/types/__init__.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from types import SimpleNamespace
2
+ from typing import Any, Dict, Optional, TypedDict
3
+
4
+ import torch
5
+ from torch import nn, optim
6
+ import torchmetrics as tm
7
+
8
+
9
+ class OperationMode:
10
+ TRAIN = "train"
11
+ VAL = "val"
12
+ TEST = "test"
13
+ PREDICT = "predict"
14
+
15
+
16
+ RawInputType = Dict
17
+
18
+
19
+ def nested_dict_to_nested_namespace(d: dict) -> SimpleNamespace:
20
+ d_ = d.copy()
21
+
22
+ for k, v in d.items():
23
+ if isinstance(v, dict):
24
+ v = nested_dict_to_nested_namespace(v)
25
+
26
+ d_[k] = v
27
+
28
+ return SimpleNamespace(**d_)
29
+
30
+
31
+ RawInputType = TypedDict(
32
+ "RawInputType",
33
+ {
34
+ "mixture": torch.Tensor,
35
+ "sources": Dict[str, torch.Tensor],
36
+ "estimates": Optional[Dict[str, torch.Tensor]],
37
+ "metadata": Dict[str, Any],
38
+ },
39
+ total=False,
40
+ )
41
+
42
+
43
+ def input_dict(
44
+ mixture: torch.Tensor = None,
45
+ sources: Dict[str, torch.Tensor] = None,
46
+ query: torch.Tensor = None,
47
+ metadata: Dict[str, Any] = None,
48
+ modality: str = "audio",
49
+ ) -> RawInputType:
50
+
51
+ out = {
52
+ "estimates": {
53
+ k: {
54
+ modality: torch.empty(
55
+ 0,
56
+ )
57
+ }
58
+ for k, v in sources.items()
59
+ }
60
+ }
61
+
62
+ if mixture is not None:
63
+ out["mixture"] = {modality: torch.from_numpy(mixture).to(torch.float32)}
64
+
65
+ if sources is not None:
66
+ out["sources"] = {k: {modality: torch.from_numpy(v).to(torch.float32)} for k, v in sources.items()}
67
+
68
+ if query is not None:
69
+ out["query"] = {modality: torch.from_numpy(query).to(torch.float32)}
70
+
71
+ if metadata is not None:
72
+ out["metadata"] = metadata
73
+
74
+ return out
75
+
76
+
77
+ class SimpleishNamespace(SimpleNamespace):
78
+ def __init__(self, **kwargs: Any) -> None:
79
+ kwargs_ = kwargs.copy()
80
+
81
+ for k, v in kwargs.items():
82
+ if isinstance(v, dict):
83
+ v = SimpleishNamespace(**v)
84
+
85
+ kwargs_[k] = v
86
+
87
+ super().__init__(**kwargs_)
88
+
89
+ def copy(self) -> "SimpleishNamespace":
90
+ return SimpleishNamespace(**{k: v for k, v in self.__dict__.items()})
91
+
92
+ def add_subnamespace(self, name: str, **kwargs: Any) -> None:
93
+ if hasattr(self, name):
94
+ raise ValueError(f"Namespace already has attribute {name}")
95
+
96
+ setattr(self, name, SimpleishNamespace(**kwargs))
97
+
98
+ def keys(self):
99
+ return self.__dict__.keys()
100
+
101
+ def __getitem__(self, key: str) -> Any:
102
+ return self.__dict__[key]
103
+
104
+ def __setitem__(self, key: str, value: Any) -> None:
105
+ self.__dict__[key] = value
106
+
107
+ def items(self):
108
+ return self.__dict__.items()
109
+
110
+
111
+ class BatchedInputOutput(SimpleishNamespace):
112
+ mixture: torch.Tensor
113
+ sources: Dict[str, torch.Tensor]
114
+ estimates: Optional[Dict[str, torch.Tensor]]
115
+ metadata: Dict[str, Any]
116
+
117
+ def __init__(self, **kwargs: Any) -> None:
118
+ super().__init__(**kwargs)
119
+
120
+ @classmethod
121
+ def from_dict(cls, d: dict) -> "BatchedInputOutput":
122
+ return cls(**d)
123
+
124
+ def to_dict(self) -> dict:
125
+ return self.__dict__
126
+
127
+
128
+ class TensorCollection(SimpleishNamespace):
129
+ def __init__(self, **kwargs: torch.Tensor) -> None:
130
+ super().__init__(**kwargs)
131
+
132
+ def apply(self, func: Any, *args: Any, **kwargs: Any) -> "TensorCollection":
133
+ return TensorCollection(
134
+ **{k: func(v, *args, **kwargs) for k, v in self.__dict__.items()}
135
+ )
136
+
137
+ def as_stacked_tensor(self, dim: int = 0) -> torch.Tensor:
138
+ return torch.stack(list(self.__dict__.values()), dim=dim)
139
+
140
+ def as_concatenated_tensor(self, dim: int = 0) -> torch.Tensor:
141
+ return torch.cat(list(self.__dict__.values()), dim=dim)
142
+
143
+ def __getitem__(self, key: str) -> torch.Tensor:
144
+ return self.__dict__[key]
145
+
146
+
147
+ InputType = BatchedInputOutput
148
+ OutputType = BatchedInputOutput
149
+ LossOutputType = Any
150
+ MetricOutputType = Any
151
+
152
+ ModelType = nn.Module
153
+ OptimizerType = optim.Optimizer
154
+ SchedulerType = optim.lr_scheduler._LRScheduler
155
+ MetricType = tm.Metric
156
+ LossType = nn.Module
157
+
158
+ OptimizationBundle = Any
159
+
160
+ LossHandler = Any
161
+ MetricHandler = Any
162
+ AugmentationHandler = Any
163
+ InferenceHandler = Any
ev-pre-aug.ckpt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:657295888781e62ef50593002720d2edb3858b9e5bbfabf0c54f715a0da4b9e2
3
+ size 645470187
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ git+https://github.com/TEAMuP-dev/pyharp.git@v0.3.0
2
+ # model-specific deps below:
3
+ pytorch-lightning
4
+ torchmetrics
5
+ librosa
6
+ hear21passt
7
+ timm