paulpak58 orech commited on
Commit
1150afc
·
0 Parent(s):

Initial commit

Browse files

Co-authored-by: orech <orech@users.noreply.huggingface.co>

.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ license: other
4
+ tags:
5
+ - audio
6
+ - audio-codec
7
+ - pytorch
8
+ - bfloat16
9
+ ---
10
+
11
+ # Liquid Codec BF16
12
+
13
+ ## Remote-code loading
14
+
15
+ The repository includes the codec configuration and model implementation, so it can be loaded with `AutoModel`:
16
+
17
+ ```python
18
+ import torch
19
+ from transformers import AutoModel
20
+
21
+ codec = AutoModel.from_pretrained(
22
+ "LiquidAI/Liquid-Codec",
23
+ trust_remote_code=True,
24
+ dtype=torch.bfloat16,
25
+ device_map="cuda",
26
+ )
27
+ waveform = torch.randn(1, 24_000 * 3, device="cuda") # 24 kHz
28
+ codes = codec.encode(waveform).audio_codes
29
+ reconstructed = codec.decode(codes).audio_values
30
+ ```
31
+
32
+ ## Native loading and streaming
33
+
34
+ ```python
35
+ import torch
36
+ from transformers import LiquidCodecModel, LiquidCodecStreamingCache
37
+
38
+ codec = LiquidCodecModel.from_pretrained("LiquidAI/Liquid-Codec", dtype=torch.bfloat16, device_map="cuda")
39
+
40
+ waveform = torch.randn(1, 24_000 * 3, device="cuda") # 24 kHz
41
+ codes = codec.encode(waveform).audio_codes # (1, 12, 38)
42
+ reconstructed = codec.decode(codes).audio_values # (1, 38 * 1920), float32
43
+
44
+ # streaming synthesis: every chunk returns the samples that no later frame changes any more
45
+ cache = LiquidCodecStreamingCache(codec.config)
46
+ chunks = [codec.decode(codes[..., i : i + 1], cache, finalize=False).audio_values for i in range(codes.shape[-1])]
47
+ chunks.append(codec.decode(codes[..., :0], cache, finalize=True).audio_values)
48
+ streamed = torch.cat(chunks, dim=-1)
49
+ ```
config.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "transformers_version": "5.19.0.dev0",
3
+ "architectures": [
4
+ "LiquidCodecModel"
5
+ ],
6
+ "output_hidden_states": false,
7
+ "return_dict": true,
8
+ "dtype": "bfloat16",
9
+ "chunk_size_feed_forward": 0,
10
+ "is_encoder_decoder": false,
11
+ "id2label": {
12
+ "0": "LABEL_0",
13
+ "1": "LABEL_1"
14
+ },
15
+ "label2id": {
16
+ "LABEL_0": 0,
17
+ "LABEL_1": 1
18
+ },
19
+ "problem_type": null,
20
+ "encoder_dim": 512,
21
+ "encoder_conv_dim": 256,
22
+ "stft_n_fft": 1280,
23
+ "stft_hop_length": 320,
24
+ "stft_win_length": 1280,
25
+ "stft_frame_stack": 6,
26
+ "quantizer_dim": 256,
27
+ "fsq_levels": [
28
+ 8,
29
+ 7,
30
+ 6,
31
+ 6
32
+ ],
33
+ "n_q": 12,
34
+ "decoder_window": 30,
35
+ "sampling_rate": 24000,
36
+ "initializer_range": 0.02,
37
+ "model_type": "liquid_codec",
38
+ "output_attentions": false,
39
+ "sample_rate": 24000,
40
+ "frame_samples": 1920,
41
+ "auto_map": {
42
+ "AutoConfig": "configuration_liquid_codec.LiquidCodecConfig",
43
+ "AutoModel": "modeling_liquid_codec.LiquidCodecModel"
44
+ }
45
+ }
configuration_liquid_codec.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import math
16
+
17
+ from huggingface_hub.dataclasses import strict
18
+
19
+ from transformers.configuration_utils import PreTrainedConfig
20
+ from transformers.models.auto import CONFIG_MAPPING
21
+ from transformers.utils import auto_docstring
22
+
23
+
24
+ @auto_docstring(checkpoint="LiquidAI/Liquid-Codec")
25
+ @strict
26
+ class LiquidCodecConfig(PreTrainedConfig):
27
+ r"""
28
+ encoder_dim (`int`, *optional*, defaults to 512):
29
+ The width of the encoder and decoder LFM3 models.
30
+ encoder_conv_dim (`int`, *optional*, defaults to 256):
31
+ The number of channels of the STFT front end.
32
+ stft_n_fft (`int`, *optional*, defaults to 1280):
33
+ The FFT size of the analysis and synthesis transforms.
34
+ stft_hop_length (`int`, *optional*, defaults to 320):
35
+ The hop of the analysis and synthesis transforms.
36
+ stft_win_length (`int`, *optional*, defaults to 1280):
37
+ The window length of the analysis and synthesis transforms.
38
+ stft_frame_stack (`int`, *optional*, defaults to 6):
39
+ The number of STFT frames per codec frame.
40
+ quantizer_dim (`int`, *optional*, defaults to 256):
41
+ The width of the latent handed to the quantizer.
42
+ fsq_levels (`list[int]`, *optional*, defaults to `[8, 7, 6, 6]`):
43
+ The number of levels of each finite-scalar-quantization dimension; their product is the codebook size.
44
+ n_q (`int`, *optional*, defaults to 12):
45
+ The number of codebooks.
46
+ decoder_window (`int`, *optional*, defaults to 30):
47
+ The sliding attention window of the decoder LFM3 model, in 75 Hz frames.
48
+ sampling_rate (`int`, *optional*, defaults to 24000):
49
+ The sampling rate of the audio.
50
+ initializer_range (`float`, *optional*, defaults to 0.02):
51
+ The standard deviation of the truncated normal initializer of the weights.
52
+ """
53
+
54
+ model_type = "liquid_codec"
55
+
56
+ encoder_dim: int = 512
57
+ encoder_conv_dim: int = 256
58
+ stft_n_fft: int = 1280
59
+ stft_hop_length: int = 320
60
+ stft_win_length: int = 1280
61
+ stft_frame_stack: int = 6
62
+ quantizer_dim: int = 256
63
+ fsq_levels: list[int] | None = None
64
+ n_q: int = 12
65
+ decoder_window: int = 30
66
+ sampling_rate: int = 24_000
67
+ initializer_range: float = 0.02
68
+
69
+ def __post_init__(self, **kwargs):
70
+ # the released configs nest the fields under `model` and dump the two LFM3 sub-configs
71
+ for key, value in kwargs.pop("model", {}).items():
72
+ if key != "n_output_samples":
73
+ setattr(self, key, value)
74
+ for key in ("encoder_lfm_config", "decoder_lfm_config", "n_output_samples"):
75
+ kwargs.pop(key, None)
76
+ if self.fsq_levels is None:
77
+ self.fsq_levels = [8, 7, 6, 6]
78
+ self.fsq_levels = list(self.fsq_levels)
79
+ super().__post_init__(**kwargs)
80
+
81
+ @property
82
+ def codebook_size(self) -> int:
83
+ return math.prod(self.fsq_levels)
84
+
85
+ @property
86
+ def frame_size(self) -> int:
87
+ return self.stft_hop_length * self.stft_frame_stack
88
+
89
+ def lfm_config(self, sliding_window: int | None = None) -> PreTrainedConfig:
90
+ attention_type = "full_attention" if sliding_window is None else "sliding_attention"
91
+ return CONFIG_MAPPING["lfm3"](
92
+ vocab_size=1,
93
+ pad_token_id=None,
94
+ bos_token_id=None,
95
+ eos_token_id=None,
96
+ hidden_size=self.encoder_dim,
97
+ intermediate_size=2304,
98
+ num_hidden_layers=8,
99
+ num_attention_heads=8,
100
+ num_key_value_heads=2,
101
+ head_dim=64,
102
+ max_position_embeddings=131_072,
103
+ norm_eps=1e-5,
104
+ conv_L_cache=7,
105
+ conv_bias=False,
106
+ use_sandwich_norm=True,
107
+ layer_types=[attention_type if i in (2, 4, 6) else "conv" for i in range(8)],
108
+ sliding_window=sliding_window,
109
+ rope_parameters={
110
+ attention_type: {"rope_type": "default", "rope_theta": 1_000_000.0, "partial_rotary_factor": 1.0}
111
+ },
112
+ use_cache=False,
113
+ tie_word_embeddings=False,
114
+ )
115
+
116
+
117
+ __all__ = ["LiquidCodecConfig"]
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3fa4d7d9b2aef1d12a4a5be269e9d740d5bd09f471d64200b4e073f055a6cd1f
3
+ size 147143580
modeling_liquid_codec.py ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import math
16
+ from dataclasses import dataclass
17
+
18
+ import torch
19
+ import torch.nn.functional as F
20
+ from torch import nn
21
+
22
+ from transformers import initialization as init
23
+ from transformers.cache_utils import DynamicCache
24
+ from transformers.modeling_utils import PreTrainedAudioTokenizerBase
25
+ from transformers.models.auto import AutoModel
26
+ from transformers.utils import ModelOutput, auto_docstring
27
+
28
+ from .configuration_liquid_codec import LiquidCodecConfig
29
+
30
+
31
+ @auto_docstring
32
+ @dataclass
33
+ class LiquidCodecEncoderOutput(ModelOutput):
34
+ r"""
35
+ audio_codes (`torch.LongTensor` of shape `(batch_size, num_codebooks, num_frames)`):
36
+ The code of every codebook of every frame.
37
+ """
38
+
39
+ audio_codes: torch.LongTensor = None
40
+
41
+
42
+ @auto_docstring
43
+ @dataclass
44
+ class LiquidCodecDecoderOutput(ModelOutput):
45
+ r"""
46
+ audio_values (`torch.FloatTensor` of shape `(batch_size, num_samples)`):
47
+ The decoded 24 kHz audio in float32.
48
+ """
49
+
50
+ audio_values: torch.FloatTensor = None
51
+
52
+
53
+ class LiquidCodecStreamingCache:
54
+ """Carries the decoder state between chunks: the LFM3 cache and the unfinished overlap-add tail."""
55
+
56
+ def __init__(self, config: LiquidCodecConfig):
57
+ self.past_key_values = DynamicCache(config=config.lfm_config(config.decoder_window))
58
+ self.audio: torch.Tensor | None = None
59
+ self.envelope: torch.Tensor | None = None
60
+ self.started = False
61
+ self.finished = False
62
+
63
+
64
+ class LiquidCodecSTFT(nn.Module):
65
+ def __init__(self, config: LiquidCodecConfig):
66
+ super().__init__()
67
+ self.n_fft = config.stft_n_fft
68
+ self.hop_length = config.stft_hop_length
69
+ self.win_length = config.stft_win_length
70
+ self.window = nn.Buffer(torch.hann_window(self.win_length, dtype=torch.float32), persistent=False)
71
+ self.num_features = 3 * (self.n_fft // 2 + 1)
72
+
73
+ def forward(self, audio: torch.Tensor) -> torch.Tensor:
74
+ total_pad = self.win_length - self.hop_length
75
+ audio = F.pad(audio.float(), (total_pad // 2, total_pad - total_pad // 2), mode="reflect")
76
+ spec = torch.stft(
77
+ audio,
78
+ n_fft=self.n_fft,
79
+ hop_length=self.hop_length,
80
+ win_length=self.win_length,
81
+ window=self.window.float(),
82
+ center=False,
83
+ return_complex=True,
84
+ )
85
+ magnitude = spec.abs()
86
+ phase_denominator = magnitude.clamp_min(1e-5)
87
+ return torch.cat((torch.log1p(magnitude), spec.real / phase_denominator, spec.imag / phase_denominator), dim=1)
88
+
89
+
90
+ class LiquidCodecFrontEnd(nn.Module):
91
+ def __init__(self, config: LiquidCodecConfig, num_features: int):
92
+ super().__init__()
93
+ self.frame_stack = config.stft_frame_stack
94
+ self.in_proj = nn.Conv1d(num_features, config.encoder_conv_dim, kernel_size=1)
95
+ self.conv = nn.Conv1d(config.encoder_conv_dim, config.encoder_conv_dim, kernel_size=3, padding=2)
96
+ self.out = nn.Conv1d(
97
+ config.encoder_conv_dim, config.encoder_dim, kernel_size=self.frame_stack, stride=self.frame_stack
98
+ )
99
+ self.act = nn.SiLU()
100
+
101
+ def forward(self, features: torch.Tensor) -> torch.Tensor:
102
+ hidden_states = self.act(self.in_proj(features.to(self.in_proj.weight.dtype)))
103
+ hidden_states = self.act(self.conv(hidden_states)[:, :, : hidden_states.shape[-1]])
104
+ return self.out(hidden_states)
105
+
106
+
107
+ class LiquidCodecLfm(nn.Module):
108
+ def __init__(self, config: LiquidCodecConfig, sliding_window: int | None = None):
109
+ super().__init__()
110
+ self.lfm = AutoModel.from_config(config.lfm_config(sliding_window))
111
+ self.lfm.register_module("embed_tokens", None)
112
+ # the projections run in the model dtype but the rotary tables stay in float32; a named hook keeps the
113
+ # module picklable
114
+ self.lfm.rotary_emb.register_forward_pre_hook(self._rotary_in_float32)
115
+
116
+ @staticmethod
117
+ def _rotary_in_float32(module, args):
118
+ return (args[0].float(), *args[1:])
119
+
120
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
121
+ return self.lfm(inputs_embeds=hidden_states, use_cache=False).last_hidden_state
122
+
123
+
124
+ class LiquidCodecQuantizer(nn.Module):
125
+ def __init__(self, config: LiquidCodecConfig):
126
+ super().__init__()
127
+ levels = list(config.fsq_levels)
128
+ self.num_codebooks = config.n_q
129
+ self.codebook_dim = len(levels)
130
+ self.levels = nn.Buffer(torch.tensor(levels, dtype=torch.int32), persistent=False)
131
+ self.basis = nn.Buffer(
132
+ torch.tensor([math.prod(levels[:index]) for index in range(self.codebook_dim)], dtype=torch.int32),
133
+ persistent=False,
134
+ )
135
+ effective_dim = self.codebook_dim * self.num_codebooks
136
+ if config.quantizer_dim == effective_dim:
137
+ self.project_in = nn.Identity()
138
+ self.project_out = nn.Identity()
139
+ else:
140
+ self.project_in = nn.Linear(config.quantizer_dim, effective_dim)
141
+ self.project_out = nn.Linear(effective_dim, config.quantizer_dim)
142
+
143
+ def _bound(self, latents: torch.Tensor, eps: float = 1e-3) -> torch.Tensor:
144
+ half_levels = (self.levels - 1) * (1 + eps) / 2
145
+ offset = torch.where(self.levels % 2 == 0, 0.5, 0.0)
146
+ shift = torch.atanh(offset / half_levels)
147
+ return torch.tanh(latents + shift) * half_levels - offset
148
+
149
+ def encode(self, latents: torch.Tensor) -> torch.Tensor:
150
+ latents = self.project_in(latents.movedim(1, -1).contiguous()).float()
151
+ latents = latents.reshape(*latents.shape[:-1], self.num_codebooks, self.codebook_dim)
152
+ half_width = self.levels // 2
153
+ codes = self._bound(latents).round() / half_width
154
+ indices = ((codes * half_width + half_width) * self.basis).sum(-1).round().long()
155
+ return indices.transpose(1, 2)
156
+
157
+ def decode(self, codes: torch.Tensor) -> torch.Tensor:
158
+ indices = codes.transpose(1, 2).unsqueeze(-1)
159
+ half_width = self.levels // 2
160
+ latents = ((indices // self.basis) % self.levels - half_width) / half_width
161
+ latents = latents.reshape(*latents.shape[:-2], -1)
162
+ if isinstance(self.project_out, nn.Linear):
163
+ latents = latents.to(self.project_out.weight.dtype)
164
+ return self.project_out(latents).movedim(-1, 1)
165
+
166
+
167
+ class LiquidCodecISTFT(nn.Module):
168
+ def __init__(self, config: LiquidCodecConfig):
169
+ super().__init__()
170
+ self.n_fft = config.stft_n_fft
171
+ self.hop_length = config.stft_hop_length
172
+ self.win_length = config.stft_win_length
173
+ self.window = nn.Buffer(torch.hann_window(self.win_length, dtype=torch.float32))
174
+
175
+ def forward(self, spec: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
176
+ num_frames = spec.shape[-1]
177
+ frames = torch.fft.irfft(spec, self.n_fft, dim=1, norm="backward") * self.window[None, :, None]
178
+ output_size = (num_frames - 1) * self.hop_length + self.win_length
179
+ fold = {"output_size": (1, output_size), "kernel_size": (1, self.win_length), "stride": (1, self.hop_length)}
180
+ audio = F.fold(frames, **fold)[:, 0, 0]
181
+ envelope = F.fold(self.window.square().expand(1, num_frames, -1).transpose(1, 2), **fold)[0, 0, 0]
182
+ return audio, envelope
183
+
184
+
185
+ class LiquidCodecDecoder(nn.Module):
186
+ def __init__(self, config: LiquidCodecConfig):
187
+ super().__init__()
188
+ self.frame_stack = config.stft_frame_stack
189
+ self.lfm = LiquidCodecLfm(config, sliding_window=config.decoder_window).lfm
190
+ self.lin = nn.Linear(config.encoder_dim, 2 * (config.stft_n_fft // 2 + 1))
191
+ self.istft = LiquidCodecISTFT(config)
192
+
193
+ def forward(
194
+ self, embeddings: torch.Tensor, past_key_values: DynamicCache | None = None
195
+ ) -> tuple[torch.Tensor, torch.Tensor]:
196
+ hidden_states = self.lfm(
197
+ inputs_embeds=F.interpolate(
198
+ embeddings.mT, embeddings.shape[1] * self.frame_stack, mode="nearest-exact"
199
+ ).mT,
200
+ past_key_values=past_key_values,
201
+ use_cache=past_key_values is not None,
202
+ ).last_hidden_state
203
+ log_magnitude, angle = torch.chunk(self.lin(hidden_states).mT.contiguous(), 2, 1)
204
+ return self.istft(torch.polar(log_magnitude.float().exp(), angle.float()))
205
+
206
+
207
+ @auto_docstring
208
+ class LiquidCodecModel(PreTrainedAudioTokenizerBase):
209
+ config: LiquidCodecConfig
210
+ base_model_prefix = "codec"
211
+ main_input_name = "input_values"
212
+ _keep_in_fp32_modules_strict = ["stft", "istft"]
213
+ _supports_flash_attn = False
214
+ _supports_sdpa = True
215
+
216
+ @classmethod
217
+ def from_pretrained(cls, *args, **kwargs):
218
+ # Remote models do not inherit Transformers' built-in checkpoint key conversions.
219
+ kwargs.setdefault(
220
+ "key_mapping",
221
+ {r"^_frontend\.": "frontend.", r"^quantizer\.fsq\.": "quantizer."},
222
+ )
223
+ return super().from_pretrained(*args, **kwargs)
224
+
225
+ def __init__(self, config: LiquidCodecConfig):
226
+ super().__init__(config)
227
+ self.stft = LiquidCodecSTFT(config)
228
+ self.frontend = LiquidCodecFrontEnd(config, self.stft.num_features)
229
+ self.encoder = LiquidCodecLfm(config)
230
+ self.to_quantizer = nn.Linear(config.encoder_dim, config.quantizer_dim)
231
+ self.quantizer = LiquidCodecQuantizer(config)
232
+ self.decoder = LiquidCodecDecoder(config)
233
+ self.from_quantizer = nn.Linear(config.quantizer_dim, config.encoder_dim)
234
+ self.post_init()
235
+
236
+ @torch.no_grad()
237
+ def _init_weights(self, module):
238
+ super()._init_weights(module)
239
+ if isinstance(module, (LiquidCodecSTFT, LiquidCodecISTFT)):
240
+ init.copy_(module.window, torch.hann_window(module.win_length, dtype=torch.float32))
241
+ elif isinstance(module, LiquidCodecQuantizer):
242
+ levels = list(self.config.fsq_levels)
243
+ init.copy_(module.levels, torch.tensor(levels, dtype=torch.int32))
244
+ init.copy_(
245
+ module.basis,
246
+ torch.tensor([math.prod(levels[:index]) for index in range(len(levels))], dtype=torch.int32),
247
+ )
248
+
249
+ def encode(self, input_values: torch.FloatTensor) -> LiquidCodecEncoderOutput:
250
+ r"""
251
+ input_values (`torch.FloatTensor` of shape `(batch_size, num_samples)`):
252
+ 24 kHz audio. The tail is zero-padded to a whole codec frame of 1920 samples.
253
+ """
254
+ if input_values.ndim != 2 or input_values.shape[-1] == 0:
255
+ raise ValueError(
256
+ f"Expected non-empty audio of shape (batch_size, num_samples), got {tuple(input_values.shape)}"
257
+ )
258
+ frame_size = self.config.frame_size
259
+ num_samples = -(input_values.shape[-1] // -frame_size) * frame_size
260
+ input_values = F.pad(input_values, (0, num_samples - input_values.shape[-1]))
261
+ hidden_states = self.frontend(self.stft(input_values)).mT
262
+ latents = self.to_quantizer(self.encoder(hidden_states)).mT
263
+ return LiquidCodecEncoderOutput(audio_codes=self.quantizer.encode(latents))
264
+
265
+ def decode(
266
+ self,
267
+ audio_codes: torch.LongTensor,
268
+ streaming_cache: LiquidCodecStreamingCache | None = None,
269
+ finalize: bool = True,
270
+ ) -> LiquidCodecDecoderOutput:
271
+ r"""
272
+ audio_codes (`torch.LongTensor` of shape `(batch_size, num_codebooks, num_frames)`):
273
+ The codes to decode; every frame becomes 1920 samples.
274
+ streaming_cache ([`LiquidCodecStreamingCache`], *optional*):
275
+ The state carried between consecutive chunks of one stream. The chunks decode to the same audio as the
276
+ concatenated codes.
277
+ finalize (`bool`, *optional*, defaults to `True`):
278
+ Whether this is the last chunk of the stream. Until then the trailing samples that later frames still
279
+ contribute to are held back in the cache.
280
+ """
281
+ if audio_codes.ndim != 3 or audio_codes.shape[1] != self.config.n_q:
282
+ raise ValueError(
283
+ f"Expected codes of shape (batch_size, {self.config.n_q}, num_frames), got {tuple(audio_codes.shape)}"
284
+ )
285
+ if audio_codes.numel() and bool(((audio_codes < 0) | (audio_codes >= self.config.codebook_size)).any()):
286
+ raise ValueError(f"Codes must be in [0, {self.config.codebook_size})")
287
+ if streaming_cache is not None and streaming_cache.finished:
288
+ raise ValueError("This stream is finalized; start with a new cache")
289
+ trim = (self.config.stft_win_length - self.config.stft_hop_length) // 2
290
+ tail = 2 * trim
291
+ batch_size = audio_codes.shape[0]
292
+ if not audio_codes.shape[-1] and not (finalize and streaming_cache is not None and streaming_cache.started):
293
+ # no frames and no held-back tail to flush: the overlap-add state must stay untouched
294
+ if streaming_cache is not None and finalize:
295
+ streaming_cache.finished = True
296
+ return LiquidCodecDecoderOutput(audio_values=audio_codes.new_zeros((batch_size, 0), dtype=torch.float32))
297
+ if audio_codes.shape[-1]:
298
+ embeddings = self.from_quantizer(self.quantizer.decode(audio_codes).mT)
299
+ past_key_values = None if streaming_cache is None else streaming_cache.past_key_values
300
+ audio, envelope = self.decoder(embeddings, past_key_values)
301
+ else:
302
+ audio = self.decoder.istft.window.new_zeros(batch_size, tail)
303
+ envelope = self.decoder.istft.window.new_zeros(tail)
304
+ if streaming_cache is None:
305
+ audio, envelope = audio[:, trim:-trim], envelope[trim:-trim]
306
+ else:
307
+ if streaming_cache.audio is not None:
308
+ audio = torch.cat([audio[:, :tail] + streaming_cache.audio, audio[:, tail:]], dim=1)
309
+ envelope = torch.cat([envelope[:tail] + streaming_cache.envelope, envelope[tail:]])
310
+ if not streaming_cache.started:
311
+ audio, envelope = audio[:, trim:], envelope[trim:]
312
+ streaming_cache.started = True
313
+ if finalize:
314
+ audio, envelope = audio[:, :-trim], envelope[:-trim]
315
+ streaming_cache.audio, streaming_cache.envelope = None, None
316
+ streaming_cache.finished = True
317
+ else:
318
+ streaming_cache.audio, streaming_cache.envelope = audio[:, -tail:], envelope[-tail:]
319
+ audio, envelope = audio[:, :-tail], envelope[:-tail]
320
+ return LiquidCodecDecoderOutput(audio_values=(audio / envelope).float())
321
+
322
+ @auto_docstring
323
+ def forward(self, input_values: torch.FloatTensor, **kwargs) -> tuple[torch.FloatTensor, torch.LongTensor]:
324
+ r"""
325
+ input_values (`torch.FloatTensor` of shape `(batch_size, num_samples)`):
326
+ 24 kHz audio.
327
+
328
+ Returns:
329
+ The reconstructed audio of the input length and the codes.
330
+ """
331
+ audio_codes = self.encode(input_values).audio_codes
332
+ audio = self.decode(audio_codes).audio_values
333
+ return audio[..., : input_values.shape[-1]], audio_codes
334
+
335
+
336
+ __all__ = ["LiquidCodecModel", "LiquidCodecStreamingCache"]