You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
60 lines
2.4 KiB
Python
60 lines
2.4 KiB
Python
2 years ago
|
# paura_lite:
|
||
|
# An ultra-simple command-line audio recorder with real-time
|
||
|
# spectrogram visualization
|
||
|
|
||
|
import numpy as np
|
||
|
import pyaudio
|
||
|
import struct
|
||
|
import scipy.fftpack as scp
|
||
|
import termplotlib as tpl
|
||
|
import os
|
||
|
|
||
|
# get window's dimensions
|
||
|
#rows, columns = os.popen('stty size', 'r').read().split()
|
||
|
rows = int(os.popen('mode con | findstr Lines','r').read().strip('\n').strip(' ').split(':')[1].strip(' '))
|
||
|
columns = int(os.popen('mode con | findstr Columns','r').read().strip('\n').strip(' ').split(':')[1].strip(' '))
|
||
|
|
||
|
buff_size = 0.2 # window size in seconds
|
||
|
wanted_num_of_bins = 40 # number of frequency bins to display
|
||
|
|
||
|
# initialize soundcard for recording:
|
||
|
fs = 8000
|
||
|
pa = pyaudio.PyAudio()
|
||
|
stream = pa.open(format=pyaudio.paInt16, channels=1, rate=fs,
|
||
|
input=True, frames_per_buffer=int(fs * buff_size))
|
||
|
|
||
|
while 1: # for each recorded window (until ctr+c) is pressed
|
||
|
# get current block and convert to list of short ints,
|
||
|
block = stream.read(int(fs * buff_size))
|
||
|
format = "%dh" % (len(block) / 2)
|
||
|
shorts = struct.unpack(format, block)
|
||
|
|
||
|
# then normalize and convert to numpy array:
|
||
|
x = np.double(list(shorts)) / (2**15)
|
||
|
seg_len = len(x)
|
||
|
|
||
|
# get total energy of the current window and compute a normalization
|
||
|
# factor (to be used for visualizing the maximum spectrogram value)
|
||
|
energy = np.mean(x ** 2)
|
||
|
max_energy = 0.02 # energy for which the bars are set to max
|
||
|
max_width_from_energy = int((energy / max_energy) * int(columns)) + 1
|
||
|
if max_width_from_energy > int(columns) - 10:
|
||
|
max_width_from_energy = int(columns) - 10
|
||
|
|
||
|
# get the magnitude of the FFT and the corresponding frequencies
|
||
|
X = np.abs(scp.fft(x))[0:int(seg_len/2)]
|
||
|
freqs = (np.arange(0, 1 + 1.0/len(X), 1.0 / len(X)) * fs / 2)
|
||
|
|
||
|
# ... and resample to a fix number of frequency bins (to visualize)
|
||
|
wanted_step = (int(freqs.shape[0] / wanted_num_of_bins))
|
||
|
freqs2 = freqs[0::wanted_step].astype('int')
|
||
|
X2 = np.mean(X.reshape(-1, wanted_step), axis=1)
|
||
|
|
||
|
# plot (freqs, fft) as horizontal histogram:
|
||
|
fig = tpl.figure()
|
||
|
fig.barh(X2, labels=[str(int(f)) + " Hz" for f in freqs2[0:-1]],
|
||
|
show_vals=False, max_width=max_width_from_energy)
|
||
|
fig.show()
|
||
|
# add exactly as many new lines as they are needed to
|
||
|
# fill clear the screen in the next iteration:
|
||
|
print("\n" * (int(rows) - freqs2.shape[0] - 1))
|