Source code for paddlespeech.t2s.modules.pqmf

# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Modified from espnet(https://github.com/espnet/espnet)
"""Pseudo QMF modules."""
import numpy as np
import paddle
import paddle.nn.functional as F
from paddle import nn
from scipy.signal import kaiser


[docs]def design_prototype_filter(taps=62, cutoff_ratio=0.142, beta=9.0): """Design prototype filter for PQMF. This method is based on `A Kaiser window approach for the design of prototype filters of cosine modulated filterbanks`_. Args: taps (int): The number of filter taps. cutoff_ratio (float): Cut-off frequency ratio. beta (float): Beta coefficient for kaiser window. Returns: ndarray: Impluse response of prototype filter (taps + 1,). .. _`A Kaiser window approach for the design of prototype filters of cosine modulated filterbanks`: https://ieeexplore.ieee.org/abstract/document/681427 """ # check the arguments are valid assert taps % 2 == 0, "The number of taps mush be even number." assert 0.0 < cutoff_ratio < 1.0, "Cutoff ratio must be > 0.0 and < 1.0." # make initial filter omega_c = np.pi * cutoff_ratio with np.errstate(invalid="ignore"): h_i = np.sin(omega_c * (np.arange(taps + 1) - 0.5 * taps)) / ( np.pi * (np.arange(taps + 1) - 0.5 * taps)) h_i[taps // 2] = np.cos(0) * cutoff_ratio # fix nan due to indeterminate form # apply kaiser window w = kaiser(taps + 1, beta) h = h_i * w return h
[docs]class PQMF(nn.Layer): """PQMF module. This module is based on `Near-perfect-reconstruction pseudo-QMF banks`_. .. _`Near-perfect-reconstruction pseudo-QMF banks`: https://ieeexplore.ieee.org/document/258122 """ def __init__(self, subbands=4, taps=62, cutoff_ratio=0.142, beta=9.0): """Initilize PQMF module. The cutoff_ratio and beta parameters are optimized for #subbands = 4. See dicussion in https://github.com/kan-bayashi/ParallelWaveGAN/issues/195. Args: subbands (int): The number of subbands. taps (int): The number of filter taps. cutoff_ratio (float): Cut-off frequency ratio. beta (float): Beta coefficient for kaiser window. """ super().__init__() h_proto = design_prototype_filter(taps, cutoff_ratio, beta) h_analysis = np.zeros((subbands, len(h_proto))) h_synthesis = np.zeros((subbands, len(h_proto))) for k in range(subbands): h_analysis[k] = ( 2 * h_proto * np.cos((2 * k + 1) * (np.pi / (2 * subbands)) * ( np.arange(taps + 1) - (taps / 2)) + (-1)**k * np.pi / 4)) h_synthesis[k] = ( 2 * h_proto * np.cos((2 * k + 1) * (np.pi / (2 * subbands)) * ( np.arange(taps + 1) - (taps / 2)) - (-1)**k * np.pi / 4)) # convert to tensor self.analysis_filter = paddle.to_tensor( h_analysis, dtype="float32").unsqueeze(1) self.synthesis_filter = paddle.to_tensor( h_synthesis, dtype="float32").unsqueeze(0) # filter for downsampling & upsampling updown_filter = paddle.zeros( (subbands, subbands, subbands), dtype="float32") for k in range(subbands): updown_filter[k, k, 0] = 1.0 self.updown_filter = updown_filter self.subbands = subbands # keep padding info self.pad_fn = nn.Pad1D(taps // 2, mode='constant', value=0.0)
[docs] def analysis(self, x): """Analysis with PQMF. Args: x (Tensor): Input tensor (B, 1, T). Returns: Tensor: Output tensor (B, subbands, T // subbands). """ x = F.conv1d(self.pad_fn(x), self.analysis_filter) return F.conv1d(x, self.updown_filter, stride=self.subbands)
[docs] def synthesis(self, x): """Synthesis with PQMF. Args: x (Tensor): Input tensor (B, subbands, T // subbands). Returns: Tensor: Output tensor (B, 1, T). """ x = F.conv1d_transpose( x, self.updown_filter * self.subbands, stride=self.subbands) return F.conv1d(self.pad_fn(x), self.synthesis_filter)
# when converting dygraph to static graph, can not use self.pqmf.synthesis directly
[docs] def forward(self, x): return self.synthesis(x)