text: import copy
  
  import numpy as np
  import pandas as pd
  from keras.initializers import glorot_uniform
  from keras.layers import Dropout, Dense, BatchNormalization, Flatten, Conv2D, MaxPooling2D
  from keras.models import Sequential
  from scipy import signal
  from scipy.fftpack import fft
  from scipy.signal import butter, lfilter
  
  
  # Most of the Spectrograms and Inversion are taken from: https://gist.github.com/kastnerkyle/179d6e9a88202ab0a2fe
  
  def butter_bandpass(lowcut, highcut, fs, order=5):
      nyq = 0.5 * fs
      low = lowcut / nyq
      high = highcut / nyq
      b, a = butter(order, [low, high], btype='band')
      return b, a
  
  def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
      b, a = butter_bandpass(lowcut, highcut, fs, order=order)
      y = lfilter(b, a, data)
      return y
  
  def overlap(X, window_size, window_step):
      """
      Create an overlapped version of X
      Parameters
      ----------
      X : ndarray, shape=(n_samples,)
          Input signal to window and overlap
      window_size : int
          Size of windows to take
      window_step : int
          Step size between windows
      Returns
      -------
      X_strided : shape=(n_windows, window_size)
          2D array of overlapped X
      """
      if window_size % 2 != 0:
          raise ValueError("Window size must be even!")
      # Make sure there are an even number of windows before stridetricks
      append = np.zeros((window_size - len(X) % window_size))
      X = np.hstack((X, append))
  
      ws = window_size
      ss = window_step
      a = X
  
      valid = len(a) - ws
      nw = (valid) // ss
      out = np.ndarray((nw,ws),dtype = a.dtype)
  
      for i in range(nw):
          # "slide" the window along the samples
          start = i * ss
          stop = start + ws
          out[i] = a[start : stop]
  
      return out
  
  
  def stft(X, fftsize=128, step=65, mean_normalize=True, real=False,
           compute_onesided=True):
      """
      Compute STFT for 1D real valued input X
      """
      if real:
          local_fft = np.fft.rfft
          cut = -1
      else:
          local_fft = np.fft.fft
          cut = None
      if compute_onesided:
          cut = fftsize // 2
      if mean_normalize:
          X -= X.mean()
  
      X = overlap(X, fftsize, step)
      
      size = fftsize
      win = 0.54 - .46 * np.cos(2 * np.pi * np.arange(size) / (size - 1))
      X = X * win[None]
      X = local_fft(X)[:, :cut]
      return X
  
  def pretty_spectrogram(d,log = True, thresh= 5, fft_size = 512, step_size = 64):
      """
      creates a spectrogram
      log: take the log of the spectrgram
      thresh: threshold minimum power for log spectrogram
      """
      specgram = np.abs(stft(d, fftsize=fft_size, step=step_size, real=False,
          compute_onesided=True))
    
      if log == True:
          specgram /= specgram.max() # volume normalize to max 1
          specgram = np.log10(specgram) # take log
          specgram[specgram < -thresh] = -thresh # set anything less than the threshold as the threshold
      else:
          specgram[specgram < thresh] = thresh # set anything less than the threshold as the threshold
      
      return specgram
  
  
  def invert_pretty_spectrogram(X_s, log = True, fft_size = 512, step_size = 512/4, n_iter = 10):
      
      if log == True:
          X_s = np.power(10, X_s)
  
      X_s = np.concatenate([X_s, X_s[:, ::-1]], axis=1)
      X_t = iterate_invert_spectrogram(X_s, fft_size, step_size, n_iter=n_iter)
      return X_t
  
  def iterate_invert_spectrogram(X_s, fftsize, step, n_iter=10, verbose=False):
      reg = np.max(X_s) / 1E8
      X_best = copy.deepcopy(X_s)
      for i in range(n_iter):
          if verbose:
              print("Runnning iter %i" % i)
          if i == 0:
              X_t = invert_spectrogram(X_best, step, calculate_offset=True,
                                       set_zero_phase=True)
          else:
              # Calculate offset was False in the MATLAB version
              # but in mine it massively improves the result
              # Possible bug in my impl?
              X_t = invert_spectrogram(X_best, step, calculate_offset=True,
                                       set_zero_phase=False)
          est = stft(X_t, fftsize=fftsize, step=step, compute_onesided=False)
          phase = est / np.maximum(reg, np.abs(est))
          X_best = X_s * phase[:len(X_s)]
      X_t = invert_spectrogram(X_best, step, calculate_offset=True,
                               set_zero_phase=False)
      return np.real(X_t)
  
  def invert_spectrogram(X_s, step, calculate_offset=True, set_zero_phase=True):
      size = int(X_s.shape[1] // 2)
      wave = np.zeros((X_s.shape[0] * step + size))
      # Getting overflow warnings with 32 bit...
      wave = wave.astype('float64')
      total_windowing_sum = np.zeros((X_s.shape[0] * step + size))
      win = 0.54 - .46 * np.cos(2 * np.pi * np.arange(size) / (size - 1))
  
      est_start = int(size // 2) - 1
      est_end = est_start + size
      for i in range(X_s.shape[0]):
          wave_start = int(step * i)
          wave_end = wave_start + size
          if set_zero_phase:
              spectral_slice = X_s[i].real + 0j
          else:
              # already complex
              spectral_slice = X_s[i]
  
          # Don't need fftshift due to different impl.
          wave_est = np.real(np.fft.ifft(spectral_slice))[::-1]
          if calculate_offset and i > 0:
              offset_size = size - step
              if offset_size <= 0:
                  print("WARNING: Large step size >50\% detected! "
                        "This code works best with high overlap - try "
                        "with 75% or greater")
                  offset_size = step
              offset = xcorr_offset(wave[wave_start:wave_start + offset_size],
                                    wave_est[est_start:est_start + offset_size])
          else:
              offset = 0
          wave[wave_start:wave_end] += win * wave_est[
              est_start - offset:est_end - offset]
          total_windowing_sum[wave_start:wave_end] += win
      wave = np.real(wave) / (total_windowing_sum + 1E-6)
      return wave
  
  def xcorr_offset(x1, x2):
      x1 = x1 - x1.mean()
      x2 = x2 - x2.mean()
      frame_size = len(x2)
      half = frame_size // 2
      corrs = np.convolve(x1.astype('float32'), x2[::-1].astype('float32'))
      corrs[:half] = -1E30
      corrs[-half:] = -1E30
      offset = corrs.argmax() - len(x1)
      return offset
  
  
  
  
  L = 8000
  
  def custom_fft(y, fs):
      T = 1.0 / fs
      N = y.shape[0]
      yf = fft(y)
      xf = np.linspace(0.0, 1.0/(2.0*T), N//2)
      # FFT is simmetrical, so we take just the first half
      # FFT is also complex, to we take just the real part (abs)
      vals = 2.0/N * np.abs(yf[0:N//2])
      return xf, vals
  
  def log_specgram(audio, sample_rate, window_size=20,
                   step_size=10, eps=1e-10):
      nperseg = int(round(window_size * sample_rate / 1e3))
      noverlap = int(round(step_size * sample_rate / 1e3))
      freqs, times, spec = signal.spectrogram(audio,
                                      fs=sample_rate,
                                      window='hann',
                                      nperseg=nperseg,
                                      noverlap=noverlap,
                                      detrend=False)
      return freqs, times, np.log(spec.T.astype(np.float32) + eps)
    
  def pad_audio(samples, t=1, L=8000):
      if len(samples) >= t*L: return samples
      else: return np.pad(samples, pad_width=(t*L - len(samples), 0), mode='constant', constant_values=(0, 0))
  
  def chop_audio(samples, t=1, L=8000):
      if len(samples) <= t*L: return samples
      else: return samples[0:t*L] 
  
  def label_transform(labels):
      nlabels = []
      for label in labels:
          if label == '_background_noise_':
              nlabels.append('silence')
          elif label not in legal_labels:
              nlabels.append('unknown')
          else:
              nlabels.append(label)
      return pd.get_dummies(pd.Series(nlabels))
  
  
  def get_model():
      
    model = Sequential()
    model.add(Conv2D(16, kernel_size=(7, 7), name='one', activation='relu', padding='same', input_shape=(150, 160, 1), kernel_initializer=glorot_uniform(seed=0)))
    model.add(BatchNormalization(axis=3, name='bn1'))
    model.add(MaxPooling2D(name='mp1', pool_size=(2, 2)))
    model.add(Dropout( 0.25, name='do1'))
    
    model.add(Conv2D(16, kernel_size=(5, 5), name='two', activation='relu', padding='same', kernel_initializer=glorot_uniform(seed=0)))
    model.add(BatchNormalization(axis=3, name='bn2'))
    model.add(MaxPooling2D(name='mp2', pool_size=(2, 2)))
    model.add(Dropout( 0.25, name='do2'))
    
    model.add(Conv2D(32, kernel_size=(3, 3), name='three', activation='relu', padding='same', kernel_initializer=glorot_uniform(seed=0)))
    model.add(BatchNormalization(axis=3, name='bn3'))
    model.add(MaxPooling2D(name='mp3', pool_size=(2, 2)))
    model.add(Dropout( 0.25, name='do3'))
    
    model.add(Conv2D(32, kernel_size=(3, 3), name='four', activation='relu', padding='same', kernel_initializer=glorot_uniform(seed=0)))
    model.add(BatchNormalization(axis=3, name='bn4'))
    model.add(MaxPooling2D(name='mp4', pool_size=(2, 2)))
    model.add(Dropout( 0.25, name='do4'))
    
    model.add(Flatten())
    model.add(Dense(256, activation='relu'))
    model.add(Dropout(0.2))
    model.add(Dense(10, activation='softmax'))
    model.compile(loss='categorical_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])
  
    return model
  
  
  ### Parameters ###
  fft_size = 320 # window size for the FFT
  step_size = fft_size/6 # distance to slide along the window (in time)
  spec_thresh = 4 # threshold for spectrograms (lower filters out more noise)
  lowcut = 500 # Hz # Low cut for our butter bandpass filter
  highcut = 8000 # Hz # High cut for our butter bandpass filter
  # For mels
  n_mel_freq_components = 64 # number of mel frequency channels
  shorten_factor = 10 # how much should we compress the x-axis (time)
  start_freq = 300 # Hz # What frequency to start sampling our melS from 
  end_freq = 8000 # Hz # What frequency to stop sampling our melS from 
  