Inverse Modified Discrete Cosine Transform (MDCT)

    Returns the Inverse Modified Discrete Cosine Transform
    with fixed window size L of the vector of coefficients y.

    The window is based on a sine window.

    Parameters
    ----------
    y : ndarray, shape (L/2, 2 * N / L)
        The MDCT coefficients
    L : int
        The window length

    Returns
    -------
    x : ndarray, shape (N,)
        The reconstructed signal

    See also
    --------
    mdct

def imdct(y, L):
    """Inverse Modified Discrete Cosine Transform (MDCT)

    Returns the Inverse Modified Discrete Cosine Transform
    with fixed window size L of the vector of coefficients y.

    The window is based on a sine window.

    Parameters
    ----------
    y : ndarray, shape (L/2, 2 * N / L)
        The MDCT coefficients
    L : int
        The window length

    Returns
    -------
    x : ndarray, shape (N,)
        The reconstructed signal

    See also
    --------
    mdct
    """
    # Signal length
    N = y.size

    # Number of frequency channels
    K = L // 2

    # Test length
    if N % K != 0:
        raise ValueError('Input length must be a multiple of the half of '
                         'the window size')

    # Number of frames
    P = N // K
    if P < 2:
        raise ValueError('Signal too short')

    # Reshape
    temp = y
    y = np.zeros((L, P), dtype=np.float)
    y[:K, :] = temp
    del temp

    # Pre-twiddle
    aL = np.arange(L, dtype=np.float)
    y = y * np.exp((1j * np.pi * (L / 2. + 1.) / L) * aL)[:, None]

    # IFFT
    x = ifft(y, axis=0)

    # Post-twiddle
    x *= np.exp((1j * np.pi / L) * (aL + (L / 2. + 1.) / 2.))[:, None]

    # Windowing
    w_long = np.sin((np.pi / L) * (aL + 0.5))
    w_edge_L = w_long.copy()
    w_edge_L[:L // 4] = 0.
    w_edge_L[L // 4:L // 2] = 1.
    w_edge_R = w_long.copy()
    w_edge_R[L // 2:L // 2 + L // 4] = 1.
    w_edge_R[L // 2 + L // 4:L] = 0.
    x[:, 0] *= w_edge_L
    x[:, 1:-1] *= w_long[:, None]
    x[:, -1] *= w_edge_R

    # Real part and scaling
    x = math.sqrt(2. / K) * L * np.real(x)

    # Overlap and add
    def overlap_add(y, x):
        z = np.concatenate((y, np.zeros((K,))))
        z[-2 * K:] += x
        return z

    x = six.moves.reduce(overlap_add, [x[:, i] for i in range(x.shape[1])])

    # Cut edges
    x = x[K // 2:-K // 2].copy()
    return x