function [y, env] = inverseSTFT(X, parameter) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Name: inverseSTFT % Date of Revision: 06 2016 % Programmer: Christian Dittmar % % Given a valid STFT spectrogram as input, this reconstructs the corresponding % time-domain signal by means of the frame-wise inverse FFT and overlap-add % method described as LSEE-MSTFT in: % [1] Daniel W. Griffin and Jae S. Lim, "Signal estimation % from modified short-time fourier transform," IEEE % Transactions on Acoustics, Speech and Signal Processing, % vol. 32, no. 2, pp. 236-243, Apr 1984. % % Input: X the complex-valued spectrogram matrix oriented % with dimensions numBins x numFrames % parameter. % blockSize the blocksize to use during synthesis % hopSize the hopsize to use during synthesis % anaWinFunc the analysis window function % reconstMirror this switch decides whether the mirror spectrum % should be reconstructed or not % appendFrame this switch decides whethter to compensate for % zero padding or not % synWinFunc the synthesis window function (per default the % same as analysis window) % analyticSig if this is set to true, we want the analytic signal % numSamples the original number of samples % % Output: y the resynthesized signal % env the envelope used for normalization of the synthesis window % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % If you use the 'NMF toolbox' please refer to: % [2] Patricio López-Serrano, Christian Dittmar, Yiğitcan Özer, and Meinard % Müller % NMF Toolbox: Music Processing Applications of Nonnegative Matrix % Factorization % In Proceedings of the International Conference on Digital Audio Effects % (DAFx), 2019. % % License: % This file is part of 'NMF toolbox'. % https://www.audiolabs-erlangen.de/resources/MIR/NMFtoolbox/ % 'NMF toolbox' is free software: you can redistribute it and/or modify it % under the terms of the GNU General Public License as published by the % the Free Software Foundation, either version 3 of the License, or (at % your option) any later version. % % 'NMF toolbox' is distributed in the hope that it will be useful, but % WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General % Public License for more details. % % You should have received a copy of the GNU General Public License along % with 'NMF toolbox'. If not, see http://www.gnu.org/licenses/. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Check parameters %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if nargin < 1 error('Please specify STFT matrix X.'); end [numBins,numFrames] = size(X); if nargin < 2 parameter = []; end if ~isfield(parameter,'blockSize') parameter.blockSize = 2048; end if ~isfield(parameter,'hopSize') parameter.hopSize = 512; end if ~isfield(parameter,'winFunc') parameter.winFunc = hann(parameter.blockSize); end % this controls if the upper part of the spectrum is given or should be % reconctructed by 'mirroring' (flip and conjugate) of the lower spectrum if ~isfield(parameter,'reconstMirror') if numBins == parameter.blockSize parameter.reconstMirror = false; elseif (numBins < parameter.blockSize) parameter.reconstMirror = true; end end if ~isfield(parameter,'appendFrame') parameter.appendFrame = true; end if ~isfield(parameter,'analyticSig') parameter.analyticSig = false; end % for better readability, use internal variables reconstMirror = parameter.reconstMirror; appendFrame = parameter.appendFrame; analyticSig = parameter.analyticSig; blockSize = parameter.blockSize; halfBlockSize = round(blockSize/2); hopSize = parameter.hopSize; % for simplicity, we assume the analysis and synthesis windows to be equal anaWinFunc = parameter.winFunc; synWinFunc = parameter.winFunc; % this needs to be changed if we are interested in the analytic signal if (analyticSig) reconstMirror = false; scale = 2; else scale = 1; end %% set up zero vectors sigLen = numFrames*hopSize+blockSize; y = zeros(sigLen,1); %% construct normalization function for the synthesis window %% that equals to the denominator in eq. (6) in [1] winFuncProd = anaWinFunc.*synWinFunc; redundancy = round(blockSize/hopSize); %% we construct the hopSize-periodic normalization %% function that will be used applied to the synthesis window env = zeros(blockSize,1); winFuncProd = anaWinFunc.*synWinFunc; %% we begin construction of this hopSize-periodic normalization %% function already before the valid part of the signal for k = -(redundancy-1):(redundancy-1) envInd = (hopSize*(k)); winInd = [1:blockSize]'; envInd = envInd+winInd; valid = find((envInd > 0) & envInd <= blockSize); envInd = envInd(valid); winInd = winInd(valid); %% add product of analysis and synthesis window env(envInd) = env(envInd) + winFuncProd(winInd); end %% apply normalization synWinFunc = synWinFunc./env; %% this is neeed for the output construction winInd = [1:blockSize]'; %% then begin frame-wise reconstruction for k = 1:numFrames %% pick one spectral frame currSpec = X(:,k); %% introduce artificial mirror spectrum if (reconstMirror) %% usually, we take the conjugate of the lower spectrum and flip it if ~analyticSig currSpec = [currSpec;conj(flipud(currSpec(2:end-1)))]; else %% in case of a desired analytic signal, we simply put zeros currSpec = [currSpec;zeros( (blockSize/2)-1 ,1)]; end end %% go back to time domain snip = ifft(currSpec, blockSize); %% if we want the analytic signal, we can skip this treatment if ~analyticSig snip = real(snip); end %% apply reconstruction window snip = scale*snip.*synWinFunc; %% find out, where to put the samples envInd = (hopSize*(k-1)); envInd = envInd+winInd; %% and overlap add, the normalization of the synthesis window is already %% factored in y(envInd) = y(envInd) + snip; end %% if required, remove safety spaces again if appendFrame y = y(halfBlockSize+1:end-halfBlockSize); end % if this was given, then revert number of samples if isfield(parameter,'numSamples') y = y(1:parameter.numSamples); end