-
Notifications
You must be signed in to change notification settings - Fork 220
/
spec_anim.py
116 lines (92 loc) · 2.97 KB
/
spec_anim.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
'''
pip install pyAudio may not work
Instead download and install a wheel from here:
https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio
or use:
pip install pipwin
pipwin install pyaudio
pipwin is like pip, but it installs precompiled Windows binaries provided by Christoph Gohlke.
'''
# to display in separate Tk window
import matplotlib
matplotlib.use('TkAgg')
import pyaudio
import os
import struct
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from scipy.fftpack import fft
import time
from tkinter import TclError
# Counts the frames
# why the list? https://stackoverflow.com/questions/25040323/unable-to-reference-one-particular-variable-declared-outside-a-function
count = [0]
# ------------ Audio Setup ---------------
# constants
CHUNK = 1024 * 2 # samples per frame
FORMAT = pyaudio.paInt16 # audio format (bytes per sample?)
CHANNELS = 1 # single channel for microphone
RATE = 44100 # samples per second
# Signal range is -32k to 32k
# limiting amplitude to +/- 4k
AMPLITUDE_LIMIT = 4096
# pyaudio class instance
p = pyaudio.PyAudio()
# stream object to get data from microphone
stream = p.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
output=True,
frames_per_buffer=CHUNK
)
# ------------ Plot Setup ---------------
fig, (ax1, ax2) = plt.subplots(2, figsize=(15, 7))
# variable for plotting
x = np.arange(0, 2 * CHUNK, 2) # samples (waveform)
xf = np.linspace(0, RATE, CHUNK) # frequencies (spectrum)
# create a line object with random data
line, = ax1.plot(x, np.random.rand(CHUNK), '-', lw=2)
# create semilogx line for spectrum, to plot the waveform as log not lin
line_fft, = ax2.semilogx(xf, np.random.rand(CHUNK), '-', lw=2)
# format waveform axes
ax1.set_title('AUDIO WAVEFORM')
ax1.set_xlabel('samples')
ax1.set_ylabel('volume')
ax1.set_ylim(-AMPLITUDE_LIMIT, AMPLITUDE_LIMIT)
ax1.set_xlim(0, 2 * CHUNK)
plt.setp(ax1, xticks=[0, CHUNK, 2 * CHUNK], yticks=[-AMPLITUDE_LIMIT, 0, AMPLITUDE_LIMIT])
# format spectrum axes
ax2.set_xlim(20, RATE / 2)
print('stream started')
def on_close(evt):
print("Closing")
# calculate average frame rate
frame_rate = count[0] / (time.time() - start_time)
# Close the stream and terminate pyAudio
stream.stop_stream()
stream.close()
p.terminate()
print('stream stopped')
print('average frame rate = {:.0f} FPS'.format(frame_rate))
quit()
def animate(i):
# binary data
data = stream.read(CHUNK)
# Open in numpy as a buffer
data_np = np.frombuffer(data, dtype='h')
# Update the line graph
line.set_ydata(data_np)
# compute FFT and update line
yf = fft(data_np)
# The fft will return complex numbers, so np.abs will return their magnitude
line_fft.set_ydata(np.abs(yf[0:CHUNK]) / (512 * CHUNK))
# Update the number of frames
count[0] += 1
if __name__ == '__main__':
start_time = time.time()
anim = animation.FuncAnimation(fig, animate, blit=False, interval=1)
fig.canvas.mpl_connect('close_event', on_close)
plt.show()