feat(assets): SA3 音效补齐 radio 11+1 与 dungeon 13源+15pan(token 走 hf-mirror 实测可用)
- 模型 17/17 文件经镜像全量缓存(gated+token,直连本机不通;sfx.md/setup.md/asset-pipeline.md 镜像结论同步更新) - 修复迁移遗留:stable_audio_3/models/ 源码子包被 .gitignore models/ 规则误伤丢失,从上游恢复并加白名单 - radio:arg_muffled/brake_screech 等叙事件落地(接线原有 L111/L154 零改动)+ phone_ring SYN→SA3 升级;dungeon 13 源 A/B 对齐(RMS 偏差 ≤0.3dB)全量同名替换,SYN 原版备份 tools/asset_fix_backup/sa3_ab_20260902/ - 关键玩法语义达标:echo 距离档 0.83<1.43<2.18<2.98s 严格递增;one-shot 真峰值全 ≤-3dBFS(解码回读法) - check_all_assets 8 游戏全绿(radio 254/254、dungeon 303/303);playtest radio 221/dungeon 1610 全绿
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from einops import rearrange
|
||||
from torchaudio.transforms import Resample
|
||||
from .blocks import ResidualUnit, WNConv1d
|
||||
|
||||
|
||||
class AutoencoderPretransform(nn.Module):
|
||||
def __init__(self, model, scale=1.0, iterate_batch=False, chunked=False):
|
||||
super().__init__()
|
||||
self.model = model
|
||||
self.model.requires_grad_(False).eval()
|
||||
self.scale = scale
|
||||
self.downsampling_ratio = model.downsampling_ratio
|
||||
self.io_channels = model.io_channels
|
||||
self.enable_grad = False
|
||||
self.iterate_batch = iterate_batch
|
||||
self.chunked = chunked
|
||||
|
||||
def encode(self, x, **kwargs):
|
||||
return self.model.encode_audio(x, chunked=self.chunked, iterate_batch=self.iterate_batch, **kwargs) / self.scale
|
||||
|
||||
def decode(self, z, chunked=None, **kwargs):
|
||||
chunked = self.chunked if chunked is None else chunked
|
||||
return self.model.decode_audio(z * self.scale, chunked=chunked, iterate_batch=self.iterate_batch, **kwargs)
|
||||
|
||||
def fold_channels_into_batch(x):
|
||||
x = rearrange(x, 'b c ... -> (b c) ...')
|
||||
return x
|
||||
|
||||
def unfold_channels_from_batch(x, channels):
|
||||
if channels == 1:
|
||||
return x.unsqueeze(1)
|
||||
x = rearrange(x, '(b c) ... -> b c ...', c = channels)
|
||||
return x
|
||||
|
||||
|
||||
class PatchedPretransform(nn.Module):
|
||||
def __init__(self, channels, patch_size, oversampling = 1, postfilter_channels = 0, **kwargs):
|
||||
super().__init__()
|
||||
self.channels = channels
|
||||
self.patch_size = patch_size
|
||||
self.oversampling = oversampling
|
||||
|
||||
self.downsampling_ratio = patch_size
|
||||
self.io_channels = channels
|
||||
self.encoded_channels = channels * patch_size
|
||||
self.enable_grad = False
|
||||
|
||||
if self.oversampling > 1:
|
||||
self.input_upsampler = Resample(1, self.oversampling)
|
||||
self.output_downsampler = Resample(self.oversampling, 1)
|
||||
|
||||
if postfilter_channels > 0:
|
||||
self.postfilter = nn.Sequential(
|
||||
WNConv1d(in_channels=channels, out_channels=postfilter_channels, kernel_size=7, padding=3, bias=True),
|
||||
ResidualUnit(in_channels=postfilter_channels, out_channels=postfilter_channels,
|
||||
dilation=1, use_snake=True, bias = True),
|
||||
ResidualUnit(in_channels=postfilter_channels, out_channels=postfilter_channels,
|
||||
dilation=3, use_snake=True, bias = True),
|
||||
ResidualUnit(in_channels=postfilter_channels, out_channels=postfilter_channels,
|
||||
dilation=9, use_snake=True, bias = True),
|
||||
WNConv1d(in_channels=postfilter_channels, out_channels=channels, kernel_size=7, padding=3, bias=False))
|
||||
|
||||
def _pad(self, x):
|
||||
seq_len = x.shape[-1]
|
||||
pad_len = (self.patch_size - (seq_len % self.patch_size)) % self.patch_size
|
||||
if pad_len > 0:
|
||||
x = torch.cat([x, torch.zeros_like(x[:, :, :pad_len])], dim=-1)
|
||||
return x
|
||||
|
||||
def encode(self, x):
|
||||
if self.oversampling > 1:
|
||||
x = self.input_upsampler(x)
|
||||
x = self._pad(x)
|
||||
x = rearrange(x, "b c (l h) -> b (c h) l", h=self.patch_size)
|
||||
return x
|
||||
def decode(self, x):
|
||||
x = rearrange(x, "b (c h) l -> b c (l h)", h=self.patch_size)
|
||||
if hasattr(self, 'postfilter'):
|
||||
x = self.postfilter(x)
|
||||
if self.oversampling > 1:
|
||||
x = self.output_downsampler(x)
|
||||
return x
|
||||
Reference in New Issue
Block a user