7.2. Visualize Sound Signals¶
We will extend the non blocking code for playing audio to visualize the audio signal in real time while playing it. We start with a simple display of the audio signal in matplotlib. For a mono signal we plot \(x(t)\). For each buffer (chunk) of data in the audio file we plot the amplitude as function of time. For a stereo signal we plot both the left \(x_L(t)\) signal and the right signal \(x_R(t)\).
1import soundfile as sf
2import pyaudio
3import numpy as np
4import matplotlib.pyplot as plt
5import time
6
7plt.close('all')
8plt.ion()
9
10FILE = "../../data/sowhat.wav"
11
12chunk = 1024 * 2
13pa = pyaudio.PyAudio()
14
15f = sf.SoundFile(FILE)
16
17is_stereo = f.channels == 2
18if is_stereo:
19 fig, axs = plt.subplots(nrows=2, ncols=1, sharex=True)
20 xleftline, = axs[0].plot(np.arange(chunk), np.zeros(chunk), '-')
21 axs[0].set_ylim([-1,1])
22 xrightline, = axs[1].plot(np.arange(chunk), np.zeros(chunk), '-')
23 axs[1].set_ylim([-1,1])
24else:
25 fig, axs = plt.subplots(nrows=1, ncols=1)
26 xmonoline, = axs[0].plot(np.zeros(chunk), np.zeros(chunk), '-')
27 axs[0].set_ylim([-1,1])
28
29plt.show(block=False)
30
31
32def callback(in_data, frame_count, time_info, status):
33 fsx = f.read(chunk, dtype='float32')
34 if is_stereo:
35 xleft = fsx[:,0]
36 xright = fsx[:,1]
37 xleftline.set_ydata(xleft)
38 xrightline.set_ydata(xright)
39 else:
40 xmono = fsx
41 xmonoline.set_ydata(xmono)
42 return (fsx.tobytes(), pyaudio.paContinue)
43
44
45stream = pa.open(format=pyaudio.paFloat32,
46 channels=f.channels,
47 rate=f.samplerate,
48 frames_per_buffer=chunk,
49 output=True,
50 stream_callback=callback)
51
52
53stream.start_stream()
54start_time = time.time()
55duration = 120 # play the first 2 minutes
56
57while stream.is_active() and time.time() - start_time < duration:
58 # time.sleep(0.1) # just waiting for the stream to finish
59 fig.canvas.draw()
60 fig.canvas.flush_events()
61
62
63stream.stop_stream()
64stream.close()
65
66pa.terminate()
67plt.close('all')
68print('done')