Add plot input example
This commit is contained in:
parent
d9ade28a36
commit
46bb5bf3e7
|
@ -0,0 +1,119 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Plot the live microphone signal(s) with matplotlib.
|
||||
|
||||
Matplotlib and NumPy have to be installed.
|
||||
https://raw.githubusercontent.com/spatialaudio/python-sounddevice/0.4.6/examples/plot_input.py
|
||||
"""
|
||||
import argparse
|
||||
import queue
|
||||
import sys
|
||||
|
||||
from matplotlib.animation import FuncAnimation
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import sounddevice as sd
|
||||
|
||||
|
||||
def int_or_str(text):
|
||||
"""Helper function for argument parsing."""
|
||||
try:
|
||||
return int(text)
|
||||
except ValueError:
|
||||
return text
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
parser.add_argument(
|
||||
'-l', '--list-devices', action='store_true',
|
||||
help='show list of audio devices and exit')
|
||||
args, remaining = parser.parse_known_args()
|
||||
if args.list_devices:
|
||||
print(sd.query_devices())
|
||||
parser.exit(0)
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
parents=[parser])
|
||||
parser.add_argument(
|
||||
'channels', type=int, default=[1], nargs='*', metavar='CHANNEL',
|
||||
help='input channels to plot (default: the first)')
|
||||
parser.add_argument(
|
||||
'-d', '--device', type=int_or_str,
|
||||
help='input device (numeric ID or substring)')
|
||||
parser.add_argument(
|
||||
'-w', '--window', type=float, default=200, metavar='DURATION',
|
||||
help='visible time slot (default: %(default)s ms)')
|
||||
parser.add_argument(
|
||||
'-i', '--interval', type=float, default=30,
|
||||
help='minimum time between plot updates (default: %(default)s ms)')
|
||||
parser.add_argument(
|
||||
'-b', '--blocksize', type=int, help='block size (in samples)')
|
||||
parser.add_argument(
|
||||
'-r', '--samplerate', type=float, help='sampling rate of audio device')
|
||||
parser.add_argument(
|
||||
'-n', '--downsample', type=int, default=10, metavar='N',
|
||||
help='display every Nth sample (default: %(default)s)')
|
||||
args = parser.parse_args(remaining)
|
||||
if any(c < 1 for c in args.channels):
|
||||
parser.error('argument CHANNEL: must be >= 1')
|
||||
mapping = [c - 1 for c in args.channels] # Channel numbers start with 1
|
||||
q = queue.Queue()
|
||||
|
||||
|
||||
def audio_callback(indata, frames, time, status):
|
||||
"""This is called (from a separate thread) for each audio block."""
|
||||
if status:
|
||||
print(status, file=sys.stderr)
|
||||
# Fancy indexing with mapping creates a (necessary!) copy:
|
||||
q.put(indata[::args.downsample, mapping])
|
||||
|
||||
|
||||
def update_plot(frame):
|
||||
"""This is called by matplotlib for each plot update.
|
||||
|
||||
Typically, audio callbacks happen more frequently than plot updates,
|
||||
therefore the queue tends to contain multiple blocks of audio data.
|
||||
|
||||
"""
|
||||
global plotdata
|
||||
while True:
|
||||
try:
|
||||
data = q.get_nowait()
|
||||
except queue.Empty:
|
||||
break
|
||||
shift = len(data)
|
||||
plotdata = np.roll(plotdata, -shift, axis=0)
|
||||
plotdata[-shift:, :] = data
|
||||
for column, line in enumerate(lines):
|
||||
line.set_ydata(plotdata[:, column])
|
||||
return lines
|
||||
|
||||
|
||||
try:
|
||||
if args.samplerate is None:
|
||||
device_info = sd.query_devices(args.device, 'input')
|
||||
args.samplerate = device_info['default_samplerate']
|
||||
|
||||
length = int(args.window * args.samplerate / (1000 * args.downsample))
|
||||
plotdata = np.zeros((length, len(args.channels)))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
lines = ax.plot(plotdata)
|
||||
if len(args.channels) > 1:
|
||||
ax.legend([f'channel {c}' for c in args.channels],
|
||||
loc='lower left', ncol=len(args.channels))
|
||||
ax.axis((0, len(plotdata), -1, 1))
|
||||
ax.set_yticks([0])
|
||||
ax.yaxis.grid(True)
|
||||
ax.tick_params(bottom=False, top=False, labelbottom=False,
|
||||
right=False, left=False, labelleft=False)
|
||||
fig.tight_layout(pad=0)
|
||||
|
||||
stream = sd.InputStream(
|
||||
device=args.device, channels=max(args.channels),
|
||||
samplerate=args.samplerate, callback=audio_callback)
|
||||
ani = FuncAnimation(fig, update_plot, interval=args.interval, blit=True)
|
||||
with stream:
|
||||
plt.show()
|
||||
except Exception as e:
|
||||
parser.exit(type(e).__name__ + ': ' + str(e))
|
Loading…
Reference in New Issue