///|
pub fn stft(
  signal : ArrayView[Double],
  window_kind : WindowKind,
  window_length : Int,
  hop_size : Int,
) -> Array[Array[Complex]] raise SpectrumError {
  if window_length <= 0 {
    raise InvalidArgument(message="window_length must be positive")
  }
  if hop_size <= 0 {
    raise InvalidArgument(message="hop_size must be positive")
  }
  if !is_power_of_two(window_length) {
    raise NonPowerOfTwo(length=window_length)
  }
  if signal.length() < window_length {
    raise InvalidArgument(
      message="signal length must be at least window_length",
    )
  }

  let win = window(window_kind, window_length)
  let out : Array[Array[Complex]] = []

  let mut start = 0
  while start + window_length <= signal.length() {
    let end_idx = start + window_length
    let frame = signal[start:end_idx]
    let windowed = apply_window(frame, win)
    let complex_frame = real_signal(windowed)
    let fft_frame = fft(complex_frame)
    out.push(fft_frame)
    start = start + hop_size
  }
  out
}