You can generate a signal with frequency f with sin(2*pi*f*t) where t is time. Yet, when I sweep the frequency from 0 to 10KHz, my signal reaches 20kHz.
Here is the instantaneous FFT: 
And the code you can use to reproduce:
import numpy as np from scipy import signal import matplotlib.pyplot as plt duration = 10 # Duration of the audio in seconds sample_rate = 44100 # Sample rate in Hz t = np.linspace(0, duration, int(sample_rate * duration)) # Generate sine wave with increasing frequency frequency_start = 10 # Start frequency in Hz frequency_end = 10000 # End frequency in Hz sine_wave = np.sin(2 * np.pi * np.linspace(frequency_start, frequency_end, len(t)) * t) # Compute STFT window_size = 1024 hop_length = 256 frequencies, times, Z = signal.stft( sine_wave, fs=sample_rate, nperseg=window_size, noverlap=window_size-hop_length ) # Plot spectrogram plt.figure(figsize=(10, 6)) plt.pcolormesh(times, frequencies, 20 * np.log10(np.abs(Z)), shading='gouraud') plt.colorbar(label='Amplitude (dB)') plt.ylabel('Frequency (Hz)') plt.xlabel('Time (s)') plt.title('STFT Spectrogram') plt.show() I generated a wav file from the signal generated and played it over speakers. It's indeed going till 20 kHz.
Why does this happen? What's the correct way to generate a frequency sweep? How does scipy.signal.chirp produce the correct result?