function new_x = overlap_add2( X, filter_mat, framelength, frameshift, preemp )

% function new_x = overlap_add( X, filter_mat, framelength, frameshift, preemp )
%
% overlap-add reconstruction of a signal, from its COMPLEX-domain spectrogram "X".
%
% Inputs:
%
%   X (mandatory): 2*framelength by nframes matrix of complex values,
%     symmetric spectrogram (as given e.g. by a FFT).
%
%   filter_mat (optional): 2*framelength by nframes matrix of real or
%     complex values.  It defines a filter that you want to apply on
%     X.  For example you may give a matrix of ones and zeroes, if you
%     only want to keep certain (time, frequency) points of your
%     spectrogram.
%
%    framelength (mandatory): integer, length of your time frame, in samples.
%
%    frameshift (mandatory): integer, shift between two time frames, in samples.
%
%    preemp (optional, default 0.97): pre-emphasis coefficient.
%
%
% Output:
%
%    new_x: vector of real values, time-domain waveform reconstructed
%      from the (optionally filtered) complex spectrogram X.
%
% By Guillaume Lathoud 2005 - lathoud@idiap.ch
% 
% Thanks to Oliver Masson for all the explanations.


if nargin < 4
   error( [ mfilename ' needs at least 4 input arguments.' ] ); 
end

if ~exist( 'preemp', 'var' )
    preemp = 0.97;
end

% XXX slightly better this way
% (on tests we found it slightly closer to the original signal)
if isempty( filter_mat )
  filter_mat = ones( size( X ) );
end

if ~isempty( filter_mat )
  if numel( unique( [ size( X, 1 ) size( filter_mat, 1 ) 2*framelength ] ) ) > 1
    error( [ mfilename ': inconsistent frame length.' ] ); 
  end
  
  nframes = unique( [ size( X, 2 ) size( filter_mat, 2 ) ] );
  if numel( nframes ) > 1
    error( [ mfilename ': inconsistent number of frames.' ] );     
  end 
else
  if numel( unique( [ size( X, 1 ) 2*framelength ] ) ) > 1
    error( [ mfilename ': inconsistent frame length.' ] ); 
  end  
  nframes = size( X,2 );
end

% overlap-add construction of a signal

% scaling + masking

if ~isempty( filter_mat )

  new_filter = ifft( filter_mat, 2*framelength, 1 );
  new_filter = new_filter( [ end-framelength/2+1:end 1:framelength/2 ], : );
  new_filter = diag( sparse( blackman( framelength, 'periodic' ) ) ) * new_filter;
  new_filter( framelength+1:2*framelength, : ) = 0;
  
  % Apply the filter in a correct manner, in the frequency domain
  new_X = X .* fft( new_filter, 2*framelength, 1 );
  
else
  
  new_X = X;

end

% overlap-add
nsamples = framelength + ( nframes-1 ) * frameshift;
new_x = zeros( nsamples+4*framelength, 1 );
the_long_win = hamming( 2*framelength, 'periodic' );
for a = 1:nframes
      
    % The "real" is for very very small numerical imprecision
    % in order to remove some 1e-10-like imaginary part
    a_frame = real( ifft( new_X( :,a ), 2 * framelength )) ./ the_long_win;
    start_sample = 1 + (a-1) * frameshift;
    end_sample   = start_sample + 2 * framelength - 1;
    ind = start_sample:end_sample;
    new_x( ind ) = new_x( ind ) + a_frame;
end

% Finally remove pre-emphasis
new_x = filter( 1, [ 1 -preemp], new_x );
  
if ~isempty( filter_mat )
  % Compensate the filter's delay: cut the crap at the beginning
  new_x = new_x( framelength/2+1:end );
end

% Cut off the crap at the end
new_x = new_x( 1:nsamples );

% Return a column vector
new_x = new_x(:);
