//  [python]
def _spsi(msgram, fftsize, hop_length, center=True) :
    """
    Takes a 2D spectrogram ([freqs,frames]), the fft legnth (= window length) and the hop size (both in units of samples).
    Returns an audio signal.

    Single Pass Spectrogram Inversion
    Reconstruct an audio signal from a magnitude spectrum with but a single ifft.
    
    Cite and more info: 
    Beauregard, G., Harish, M. and Wyse, L. (2015), Single Pass Spectrogram Inversion, 
    in Proceedings of the IEEE International Conference on Digital Signal Processing. Singapore, 2015.

    source: https://github.com/lonce/SPSI_Python/blob/master/SPSI_notebook/spsi.ipynb
    """
    
    numBins, numFrames  = msgram.shape

    # y_out=np.zeros(numFrames*hop_length+fftsize-hop_length)

    expected_signal_len = fftsize + hop_length * (numFrames - 1)
    # print '_spsi: expected_signal_len:', expected_signal_len
    y_out=np.zeros(expected_signal_len)
        
    m_phase=np.zeros(numBins);      
    m_win=scipy.signal.windows.hann(fftsize, sym=True)  # assumption here that hann was used to create the frames of the spectrogram
    
    #processes one frame of audio at a time
    for i in range(numFrames) :
        m_mag=msgram[:, i] 
        for j in range(1,numBins-1) : 
            if(m_mag[j]>m_mag[j-1] and m_mag[j]>m_mag[j+1]) : #if j is a peak
                alpha=m_mag[j-1];
                beta=m_mag[j];
                gamma=m_mag[j+1];
                denom=alpha-2*beta+gamma;
                
                if(denom!=0) :
                    p=0.5*(alpha-gamma)/denom;
                else :
                    p=0;
                    
                #phaseRate=2*math.pi*(j-1+p)/fftsize;    #adjusted phase rate
                phaseRate=2*math.pi*(j+p)/fftsize;    #adjusted phase rate
                m_phase[j]= m_phase[j] + hop_length*phaseRate; #phase accumulator for this peak bin
                peakPhase=m_phase[j];
                
                # If actual peak is to the right of the bin freq
                if (p>0) :
                    # First bin to right has pi shift
                    bin=j+1;
                    m_phase[bin]=peakPhase+math.pi;
                    
                    # Bins to left have shift of pi
                    bin=j-1;
                    while((bin>1) and (m_mag[bin]<m_mag[bin+1])) : # until you reach the trough
                        m_phase[bin]=peakPhase+math.pi;
                        bin=bin-1;
                    
                    #Bins to the right (beyond the first) have 0 shift
                    bin=j+2;
                    while((bin<(numBins)) and (m_mag[bin]<m_mag[bin-1])) :
                        m_phase[bin]=peakPhase;
                        bin=bin+1;
                        
                #if actual peak is to the left of the bin frequency
                if(p<0) :
                    # First bin to left has pi shift
                    bin=j-1;
                    m_phase[bin]=peakPhase+math.pi;

                    # and bins to the right of me - here I am stuck in the middle with you
                    bin=j+1;
                    while((bin<(numBins)) and (m_mag[bin]<m_mag[bin-1])) :
                        m_phase[bin]=peakPhase+math.pi;
                        bin=bin+1;
                    
                    # and further to the left have zero shift
                    bin=j-2;
                    while((bin>1) and (m_mag[bin]<m_mag[bin+1])) : # until trough
                        m_phase[bin]=peakPhase;
                        bin=bin-1;
                        
            #end ops for peaks
        #end loop over fft bins with

        magphase=m_mag*np.exp(1j*m_phase)  #reconstruct with new phase (elementwise mult)
        magphase[0]=0; magphase[numBins-1] = 0 #remove dc and nyquist
        m_recon=np.concatenate([magphase,np.flip(np.conjugate(magphase[1:numBins-1]), 0)]) 
        
        #overlap and add
        m_recon=np.real(np.fft.ifft(m_recon))*m_win
        y_out[i*hop_length:i*hop_length+fftsize]+=m_recon
            
    if center:
        y_out = y_out[int(fftsize // 2):-int(fftsize // 2)]

    return y_out