Basic Signals and Systems in Python
The following examples introduce file operations and signal generators commonly used in DSP with Python. Import these modules before running any of the code snippets below:
import numpy as np
import scipy
import matplotlib.pyplot as plt
Implementing Basic Signals¶
#White Noise with numpy
whiteNoise = np.random.normal(loc=0.0, scale=1.0, size=48000)
#Normalize to the peak amplitude of the signal
peakAmplitude = np.max(np.abs(whiteNoise))
whiteNoise = whiteNoise/peakAmplitude
#A Time Axis
timeAxis = np.linspace(0,1,48000)
plt.plot(timeAxis, whiteNoise)
plt.xlabel("t/s")
plt.ylabel("x(t)")
plt.title("Stochastic Noise over 1 second")
plt.show()
Loading a Wavfile with SciPy¶
We are using scipy.io.wavfile.read
, you will find the documentation here:
https://docs.scipy.org/doc/scipy-1.15.0/reference/generated/scipy.io.wavfile.read.html
The file in the example below needs to be placed in the same directory, Python is currently running in. This should be the same directory the script is located in: https://ringbuffer.org/download/audio/210321_011_Raven.wav
Feel free to download other audio files from freesound: https://freesound.org or use any wav files on your computer.
#Loading a signal with scipy
from scipy.io import wavfile
wavPath = "418101__buzei__vroxi.wav"
samplerate, data = wavfile.read(wavPath);
# Ensure we only take one channel, if signal is stereo
if data.ndim == 1:
data = data
else:
data = data[:, 0]
#A time axis
timeAxis = np.linspace(0,1,samplerate)
#Normalize to the peak amplitude of the signal
peakAmplitude = np.max(np.abs(data))
data = data/peakAmplitude
#plotting the loaded waveform
plt.plot(timeAxis, data[0:len(timeAxis)])
plt.xlabel("t/s")
plt.ylabel("amplitude x(t)")
plt.title("Rain Noise over 1 second")
plt.show()
/tmp/ipykernel_37451/157922970.py:6: WavFileWarning: Chunk (non-data) not understood, skipping it. samplerate, data = wavfile.read(wavPath);
Generate A Sine Wave, Periodic Signal¶
samplerate = 1000 # 1000 points every second
# 1 second of audio
timeAxis = np.linspace(0, 1, samplerate)
freq = 5
sineWave = 0.5*np.sin(2*np.pi*freq*timeAxis)
plt.plot(timeAxis, sineWave)
plt.xlabel("t/s")
plt.ylabel("x(t)")
plt.title("5 Hz Sine Wave, 1 second")
plt.show()
Writing a Wavfile¶
We are using scipy.io.wavfile.write
, you will find the documentation here: https://docs.scipy.org/doc/scipy-1.15.0/reference/generated/scipy.io.wavfile.write.html
from scipy.io.wavfile import write
samplerate = 48000 #48000 points every second
# 3 second of audio
timeInSeconds = 4
timeAxis = np.linspace(0, timeInSeconds, timeInSeconds*samplerate)
freq = 200
sineWave = 0.5*np.sin(2*np.pi*freq*timeAxis)
fileName = "frstSine.wav"
write(fileName, samplerate, sineWave)
Activity - Load and Observe some Quasi-Periodic Waveforms¶
Explore sounds from freesound:https://freesound.org , load the waveform and visualize it. Use the Probability Mass Function to analyze it.
Plot some:
- Transients (Snare drum, hand clap)
- Textures (Rain, Fire Crackling)
- Speech Signals
- Music Signals
Probability Mass Function¶
# We use numpy histogram to get the relative count
# values resulting from the given random distribution
count, bin_edges = np.histogram(whiteNoise, bins=30, density=False)
# The count array is then converted to probabilities
pmf = count / np.sum(count)
# Making things a little neater in the plot by computing bin centers (Best Practice!)
bin_centers = 0.5 * (bin_edges[:-1] + bin_edges[1:])
# Plot
plt.figure(figsize=(8,5))
# Review the matplotlib.plt.bar documentation
plt.bar(bin_centers, pmf ,width=(bin_edges[1] - bin_edges[0]), alpha=0.7, edgecolor='k')
plt.title('Probability mass function')
plt.xlabel('x')
plt.ylabel('p(x)')
plt.show()
# Check that the PMF sums to 1
print("Sum of PMF values =", np.sum(pmf))
Sum of PMF values = 1.0
Implementing Basic Sequences¶
# Impulse
impulse = np.zeros(20)
impulse[0] = 1
plt.stem(impulse)
plt.xlabel("n")
plt.ylabel("x[n]")
plt.title("Impulse")
plt.show()
#note: Self practice: Try shifting the impulse and experimenting with
# the properties of an impulse signal
#Heaviside Step Function
# Generate the step values
step_axis = np.linspace(-10, 10, 20)
step = np.where(step_axis >= 0, 1, 0)
plt.stem(step_axis,step)
plt.xlabel("n")
plt.ylabel("x[n]")
plt.title("Heaviside")
plt.show()
# Pulse
step = np.zeros(20)
step[6:14] = 1
plt.stem(step)
plt.xlabel("n")
plt.ylabel("x[n}")
plt.title("Pulse")
plt.show()
# Linear Ramp
linearRamp = np.arange(0,20)
plt.stem(linearRamp)
plt.xlabel("n")
plt.ylabel("x[n]")
plt.title("Linear Ramp Sequence")
plt.show()
#note: Self practice -> How would you make this exponential??
# Can this work with linspace?
Common Signal Operations¶
# Implement ADSR On The Previously Generated Sine
samplerate = 1000 # 1000 points every second
# 1 second of audio
timeAxis = np.linspace(0, 1, samplerate)
freq = 5
sineWave = 0.5*np.sin(2*np.pi*freq*timeAxis)
plt.plot(timeAxis, sineWave)
plt.xlabel("t/s")
plt.ylabel("x(t)")
plt.title("5 Hz Sine Wave, 1 second")
plt.show()
#Assume sustain level is 50%
def ADSR(attackTime, decayTime, sustainTime, releaseTime, samplerate):
# An attack is a linear ramp up
attack = np.linspace(0,1,int(samplerate*attackTime))
# An Decay is a linear ramp down
decay = np.linspace(1,0.5,int(samplerate*decayTime))
#Sustain is a unit-step function
sustain = np.linspace(0.5,0.5,int(samplerate*sustainTime))
# Release is another linear ramp down
release = np.linspace(0.5,0,int(samplerate*releaseTime))
adsr = np.concatenate((attack,decay, sustain, release), axis=None)
return adsr
# Parameters in seconds
attackTime = 0.2
decayTime = 0.1
sustainTime = 0.5
releaseTime = 0.2
#Calling the function
adsr = ADSR(attackTime, decayTime, sustainTime, releaseTime, samplerate)
plt.figure()
plt.plot(timeAxis,adsr)
plt.xlabel("t/s")
plt.ylabel("x(t)")
plt.title("ADSR Curve Generated")
plt.show()
w = sineWave*adsr
plt.figure()
plt.plot(timeAxis,w)
plt.xlabel("t/s")
plt.ylabel("x(t)")
plt.title("Modulated Sine")
plt.show()
Activity: Apply an ADSR Envelope¶
-
Create your own ADSR envelope and modulate an audiorate sineWave to avoid the clicks during playback, experiment with the different parameters.
-
Write into a wavfile