
Image by Author | Canva
Audio is everywhere, from the music we stream to the voice notes we send and the calls we make. But raw audio isn’t always ready to use straight out of the mic or file. That’s where audio processing comes in.
Audio processing simply means cleaning, changing, or analyzing audio signals to make them more useful. You might want to reduce background noise, detect spoken words, increase volume, or turn sound into visual data. Whether you’re building a podcast app, working with speech recognition, or just playing around with audio effects, being able to process audio is a valuable skill.
Big companies like Spotify, Google, Apple, and Amazon all rely heavily on audio processing. Spotify uses it for music analysis and recommendations, Google and Apple use it in their voice assistants, and Amazon uses it in Alexa to understand voice commands.
In this article, you’ll learn how to use NumPy, a fast and powerful library in Python for working with audio signals. We’ll keep things simple and focus on the basics you need to get started.
Prerequisites
Before we start, there are a few libraries we need to have installed. We’ll be using NumPy for processing the audio data as arrays, matplotlib for visualizing waveforms, and scipy for resampling operations. We’ll also use pydub to load and export audio files like MP3s and WAVs easily, which helps us skip low-level decoding headaches and just focus on working with the actual audio data.
pip install numpy matplotlib scipy pydub
pydub depends on ffmpeg to handle MP3s and other formats. If it’s not already installed on your system, you can install it like this:
For macOS (using Homebrew) users:
brew install ffmpeg
For Ubuntu/Linux users:
sudo apt install ffmpeg
For Windows users:
Download the binaries from https://ffmpeg.org/download.html and add them to your system PATH. Once everything is set up, you’re ready to start working with audio in Python!
Hands-On Project: Audio Signal Processing with NumPy
Now let’s bring everything together in a project. For this, we’ll use pydub for audio handling and numpy for processing. The idea is to show you how these transformations work in practice and explain the “why” and “how” along the way. We will also perform some actions such as viewing the waveform, normalization and scaling, trimming and slicing, reversing the audio, changing Volume (Amplitude Scaling), and so much more.
Step 1: Setting Up and Loading Audio
We’ll begin by loading the audio file using pydub. This library makes it easy to read and manipulate audio files of different formats. Once we load the file, we’ll convert it to a NumPy array so we can process the waveform directly.
from pydub import AudioSegment
import numpy as np
import matplotlib.pyplot as plt
# Load your audio file (MP3, WAV, etc.)
audio_path = "/Users/user/Downloads/pianos-by-jtwayne-7-174717.mp3"
audio = AudioSegment.from_file(audio_path)
# Convert to mono to keep things simple
audio = audio.set_channels(1)
# Extract raw audio samples as a NumPy array
samples = np.array(audio.get_array_of_samples()).astype(np.float32)
# Normalize the waveform to range [-1, 1]
samples /= np.max(np.abs(samples))
# Get sample rate (frames per second)
sample_rate = audio.frame_rate
# Print summary
print(f"Audio duration: {len(samples)/sample_rate:.2f} seconds")
print(f"Sample rate: {sample_rate} Hz")
Output:
Audio duration: 18.86 seconds Sample rate: 44100 Hz
Step 2: Viewing the Waveform
Let’s plot the waveform to see what the raw audio signal looks like. This will help us understand the structure of the sound.
# Plot waveform using matplotlib
plt.figure(figsize=(12, 4))
plt.plot(samples, color='slateblue')
plt.title("Original Audio Waveform")
plt.xlabel("Sample Index")
plt.ylabel("Amplitude")
plt.grid(True)
plt.tight_layout()
plt.show()
Output:

Image showing the audio Waveform
Step 3: Normalization and Scaling
Even though we normalized earlier, let’s say you want to rescale your audio to control loudness manually.
# Scale audio to 50% volume scaled_samples = samples * 0.5
Step 4: Trimming and Slicing
Let’s trim the first and last few seconds of the audio.
# Trim audio: keep only the middle 10 seconds start_time = 5 * sample_rate # 5 seconds in samples end_time = 15 * sample_rate # 15 seconds in samples trimmed_samples = samples[int(start_time):int(end_time)]
Step 5: Reversing the Audio
Flipping the waveform gives us the reversed version of the audio.
# Reverse the audio reversed_samples = samples[::-1]
Step 6: Changing Volume (Amplitude)
You can increase or decrease volume by scaling the amplitude up or down.
# Increase volume by 1.5x (be careful of clipping!) louder_samples = samples * 1.5 # Clip values between -1 and 1 to avoid distortion louder_samples = np.clip(louder_samples, -1.0, 1.0)
Step 7: Adding a Simple Echo Effect
Let’s add a basic echo by mixing the original signal with a delayed version of itself.
# Create an echo effect delay_samples = int(0.2 * sample_rate) # 200ms delay echo = np.zeros(len(samples) + delay_samples) echo[:len(samples)] += samples echo[delay_samples:] += 0.5 * samples # lower volume for echo tail # Normalize echo echo /= np.max(np.abs(echo))
Step 8: Speed Up or Slow Down (Resampling)
Changing the speed means changing how fast we play the samples.
from scipy.signal import resample # Speed up by resampling to fewer points speed_up = resample(samples, int(len(samples) * 0.75)) # 1.33x speed # Slow down slow_down = resample(samples, int(len(samples) * 1.25)) # 0.8x speed
Step 9: Basic Pitch Shift
This is a naive pitch shift done by speeding up or slowing down — it affects both pitch and duration.
# Shift pitch up (naive approach) pitch_up = resample(samples, int(len(samples) * 0.8)) # higher pitch # Shift pitch down pitch_down = resample(samples, int(len(samples) * 1.2)) # lower pitch
Saving and Exporting Audio
Let’s save the processed audio as a file. We’ll convert it from NumPy to AudioSegment and export it.
from pydub import AudioSegment
def save_numpy_audio(np_samples, sample_rate, out_path):
# Convert float [-1,1] to int16
int_samples = (np_samples * 32767).astype(np.int16)
# Create AudioSegment from raw data
output_audio = AudioSegment(
int_samples.tobytes(),
frame_rate=sample_rate,
sample_width=2, # 16-bit audio
channels=1
)
# Export to file
output_audio.export(out_path, format="wav")
print(f"Saved audio to {out_path}")
# Save example
save_numpy_audio(echo, sample_rate, "output_with_echo.wav")
Final Thoughts
While NumPy gives you a lot of control when working with audio, it does come with a few limitations. For example, it doesn’t handle audio formats or playback by itself, you have to use other tools alongside it. Also, things like real-time processing, advanced effects, and working with compressed formats can get tricky if you stick to just NumPy.
That said, NumPy is the backbone for many popular audio libraries like librosa, scipy, and even parts of pydub. It helps you understand what’s really happening under the hood when you apply effects or manipulate sound.
If you enjoyed this project, try building something small — maybe an audio visualizer or a tool that adds cool effects to recordings. Keep experimenting. The more you play with audio data, the better your understanding will get.
