My dear, as far as I understood what you are looking for, you are getting into a theory field called Digital Signal Processing (DSP). This engineering area comes from a simple analysis of discrete-time signals to complex adaptive filters. A nice idea is to think of the discrete-time signals as a vector, where each element of this vector is a sampled value of the original, continuous-time signal. Once you get the samples in a vector form, you can apply different digital signal techniques to this vector.
Unfortunately, on Python, moving from audio files to NumPy array vector is rather cumbersome, as you could notice... If you don't idolize one programming language over other, I highly suggest trying out MatLab/Octave. Matlab makes the samples access from files straightforward. audioread()
makes this task to you :) And there are a lot of toolboxes designed specifically for DSP.
Nevertheless, if you really intend to get into Python for this, I'll give you a step-by-step to guide you.
1. Get the samples
The easiest way the get the samples from the .wav
file is:
from scipy.io import wavfile
sampling_rate, samples = wavfile.read(f'/path/to/file.wav')
Alternatively, you could use the wave
and struct
package to get the samples:
import numpy as np
import wave, struct
wav_file = wave.open(f'/path/to/file.wav', 'rb')
# from .wav file to binary data in hexadecimal
binary_data = wav_file.readframes(wav_file.getnframes())
# from binary file to samples
s = np.array(struct.unpack('{n}h'.format(n=wav_file.getnframes()*wav_file.getnchannels()), binary_data))
Answering your question: binary_data
is a bytes
object, which is not human-readable and can only make sense to a machine. You can validate this statement typing type(binary_data)
. If you really want to understand a little bit more about this bunch of odd characters, click here.
If your audio is stereo (that is, has 2 channels), you can reshape this signal to achieve the same format obtained with scipy.io
s_like_scipy = s.reshape(-1, wav_file.getnchannels())
Each column is a chanell. In either way, the samples obtained from the .wav
file can be used to plot and understand the temporal behavior of the signal.
In both alternatives, the samples obtained from the files are represented in the Linear Pulse Code Modulation (LPCM)
2. Do digital signal processing stuffs onto the audio samples
I'll leave that part up to you :) But this is a nice book to take you through DSP. Unfortunately, I don't know good books with Python, they are usually horrible books... But do not worry about it, the theory can be applied in the very same way using any programming language, as long as you domain that language.
Whatever the book you pick up, stick with the classical authors, such as Proakis, Oppenheim, and so on... Do not care about the language programming they use. For a more practical guide of DPS for audio using Python, see this page.
3. Play the filtered audio samples
import pyaudio
p = pyaudio.PyAudio()
stream = p.open(format = p.get_format_from_width(wav_file.getsampwidth()),
channels = wav_file.getnchannels(),
rate = wav_file.getframerate(),
output = True)
# from samples to the new binary file
new_binary_data = struct.pack('{}h'.format(len(s)), *s)
stream.write(new_binary_data)
where wav_file.getsampwidth()
is the number of bytes per sample, and wav_file.getframerate()
is the sampling rate. Just use the same parameters of the input audio.
4. Save the result in a new .wav
file
wav_file=wave.open('/phat/to/new_file.wav', 'w')
wav_file.setparams((nchannels, sampwidth, sampling_rate, nframes, "NONE", "not compressed"))
for sample in s:
wav_file.writeframes(struct.pack('h', int(sample)))
where nchannels
is the number of channels, sampwidth
is the number of bytes per samples, sampling_rate
is the sampling rate, nframes
is the total number of samples.