File size: 8,915 Bytes
b522cc2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import os

import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import Qwen2Config, Qwen2Model

from .istft import ISTFTHead


_AUDIO_VAE_ATTN_ENV = "MING_TINY_AUDIO_VAE_ATTN"
_AUDIO_VAE_ATTN_BACKENDS = {
    "config",
    "eager",
    "sdpa",
    "flash_attention_2",
}


def _build_qwen2_config(config_dict):
    """Build the AudioVAE backbone config with a portable attention backend.

    The training config records ``flash_attention_2`` for both AudioVAE
    backbones.  Requiring that optional binary extension just to construct the
    HF model makes the standalone checkpoint unusable on an otherwise valid
    Torch/SDPA environment.  SDPA is the safe inference default; an explicitly
    prepared runtime can opt back into FlashAttention with the environment
    variable below.
    """

    backend = os.getenv(_AUDIO_VAE_ATTN_ENV, "config").strip().lower()
    if backend not in _AUDIO_VAE_ATTN_BACKENDS:
        raise ValueError(
            f"{_AUDIO_VAE_ATTN_ENV}={backend!r} is invalid; choose one of "
            f"{sorted(_AUDIO_VAE_ATTN_BACKENDS)}"
        )
    config = Qwen2Config.from_dict(config_dict=config_dict)
    if backend != "config":
        config._attn_implementation = backend
    return config


class StreamingLinearUpsample(nn.Module):
    def __init__(self, scale_factor=4):
        super().__init__()
        self.scale_factor = scale_factor
        self.upsampler = nn.Upsample(scale_factor=scale_factor, mode='linear', align_corners=False)

    def forward(self, x, state=None, is_last=False):
        # 初始化状态
        if state is None:
            state = {
                'prev_chunk': None,
                'history_last': None,
                'is_first': True
            }

        if x is None and not is_last:
            return None, state

        if state['is_first'] and is_last:
            out = self.upsampler(x.transpose(1, 2)).transpose(1, 2)
            return out, None # 结束后清除状态

        output_chunks = []

        if state['is_first']:
            state['prev_chunk'] = x
            state['is_first'] = False
            if not is_last:
                return None, state

        if state['prev_chunk'] is not None:
            p = state['prev_chunk'].transpose(1, 2)

            if state['history_last'] is None:
                lookahead = x[:, :1, :].transpose(1, 2)
                inp = torch.cat([p, lookahead], dim=2)
                up = self.upsampler(inp)
                out_prev = up[:, :, :p.size(2) * self.scale_factor]
            else:
                lookahead = x[:, :1, :].transpose(1, 2)
                inp = torch.cat([state['history_last'], p, lookahead], dim=2)
                up = self.upsampler(inp)
                start = self.scale_factor
                end = start + p.size(2) * self.scale_factor
                out_prev = up[:, :, start:end]

            output_chunks.append(out_prev.transpose(1, 2))
            state['history_last'] = p[:, :, -1:]
            state['prev_chunk'] = x

        if is_last:
            p = state['prev_chunk'].transpose(1, 2)
            inp = torch.cat([state['history_last'], p], dim=2)
            up = self.upsampler(inp)
            out_last = up[:, :, self.scale_factor:]
            output_chunks.append(out_last.transpose(1, 2))
            state = None # 结束

        final_out = torch.cat(output_chunks, dim=1) if output_chunks else None
        return final_out, state


class Encoder(nn.Module):
    def __init__(self, encoder_args, input_dim=320, hop_size=320, latent_dim=64, patch_size=-1):
        super().__init__()
        config = _build_qwen2_config(encoder_args)
        self.encoder = Qwen2Model(config)
        self.input_dim = input_dim
        self.hop_size = hop_size
        self.latent_dim = latent_dim
        self.fc1 = nn.Linear(input_dim, config.hidden_size, bias=False)
        self.fc2 = nn.Linear(config.hidden_size, config.hidden_size)
        self.fc3 = nn.Linear(config.hidden_size, latent_dim*2)
        self.norm = nn.LayerNorm(config.hidden_size)
        self.patch_size = patch_size
        if patch_size != -1:
            config.num_hidden_layers = 4
            self.aggregator = Qwen2Model(config)
            self.cls_embed = nn.Parameter(torch.rand(1, 1, config.hidden_size))
            self.cls_embed.data.normal_(0, 0.02)

    def get_frames(self, x):
        num_frames_total = (x.size(-1) + self.hop_size - 1) // self.hop_size  # 向上取整的帧数
        expected_len = (num_frames_total - 1) * self.hop_size + self.input_dim
        padding_needed = expected_len - x.size(-1)
        waveform = F.pad(x, (0, padding_needed), value=0.0)

        frames = waveform.unfold(dimension=-1, size=self.input_dim, step=self.hop_size)   # [B, T, d]
        return frames

    def pad_patch_insert_cls(self, x):
        bsz, _, dim = x.size()
        num_frame = x.size(1)
        r = num_frame % self.patch_size
        pad_num = self.patch_size-r if r else 0
        x = F.pad(x, (0, 0, 0, pad_num), value=0.0)  # 帧数对齐到patch_size倍数
        x = x.reshape(-1, self.patch_size, dim)
        x = torch.cat((x, self.cls_embed.expand(x.size(0), -1, -1)), dim=1)  # 每个patch后插入一个cls
        x = x.reshape(bsz, -1, dim)
        return x

    def forward(self, waveform):
        x = self.get_frames(waveform)

        x = self.fc1(x)
        x = self.fc2(x)
        x = self.encoder(inputs_embeds=x)
        x = x.last_hidden_state

        # downsample
        if self.patch_size != -1:
            x = self.pad_patch_insert_cls(x)
            x = self.aggregator(inputs_embeds=x)
            x = x.last_hidden_state
            bsz, _, dim = x.size()
            x = x.reshape(-1, self.patch_size+1, dim)
            x = x[:, -1:, :].reshape(bsz, -1, dim)

        x = self.fc3(x)
        return x, waveform.unsqueeze(1)


class Decoder(nn.Module):
    def __init__(self, decoder_args, output_dim=320, latent_dim=64, semantic_model=None, patch_size=-1):
        super().__init__()
        config = _build_qwen2_config(decoder_args)
        self.decoder = Qwen2Model(config)
        self.output_dim = output_dim
        self.latent_dim = latent_dim
        self.fc1 = nn.Linear(latent_dim, config.hidden_size)

        if semantic_model is not None:
            self.gelu = nn.GELU()
            self.fc2 = nn.Linear(config.hidden_size, semantic_model.audio_emb_dim)
            self.semantic_model = semantic_model
            self.fc3 = nn.Linear(semantic_model.audio_emb_dim, config.hidden_size)
        else:
            self.semantic_model = None

        self.hop_length = output_dim
        self.head = ISTFTHead(dim=config.hidden_size, n_fft=self.hop_length * 4, hop_length=self.hop_length, padding="same")
        self.patch_size = patch_size
        if self.patch_size != -1:
            self.upsampling = StreamingLinearUpsample(scale_factor=patch_size)


    def forward(self, x, only_semantic_emb=False, past_key_values=None, use_cache=False):
        x = self.fc1(x)

        if self.semantic_model is not None:
            x = self.fc2(self.gelu(x))
            x, past_key_values = self.semantic_model(whipser_feats=x, past_key_values=past_key_values, use_cache=use_cache)
            unified_emb = x
            if only_semantic_emb:
                return unified_emb, past_key_values
            x = self.fc3(x)
        else:
            unified_emb = None

        if self.patch_size != -1:
            x = self.upsampling(x.transpose(1, 2)).transpose(1, 2)

        x = self.decoder(inputs_embeds=x)
        x = x.last_hidden_state

        x, _ = self.head(x)

        return x, unified_emb

    def low_level_reconstruct(self, x, past_key_values=None, use_cache=False, stream_state=None, last_chunk=False):
        upsample_state, audio_buffer, window_buffer = stream_state
        bsz, device, dtype = x.size(0), x.device, x.dtype
        x = self.fc1(x)
        if self.patch_size != -1:
            if use_cache:
                # streaming
                x, upsample_state = self.upsampling(x, state=upsample_state, is_last=last_chunk)
                if x is None:
                    stream_state = (upsample_state, audio_buffer, window_buffer)
                    return torch.empty(bsz, 1, 0, device=device, dtype=dtype), stream_state, past_key_values
            else:
                x = self.upsampling.upsampler(x.transpose(1, 2)).transpose(1, 2)

        outputs = self.decoder(inputs_embeds=x, past_key_values=past_key_values, use_cache=use_cache)
        past_key_values = outputs.past_key_values
        x = outputs.last_hidden_state

        x, _, audio_buffer, window_buffer = self.head(x, streaming=use_cache, audio_buffer=audio_buffer, window_buffer=window_buffer, last_chunk=last_chunk)

        stream_state = (upsample_state, audio_buffer, window_buffer)
        return x, stream_state, past_key_values