Last active
May 3, 2018 03:42
-
-
Save drscotthawley/c344ab278c17b6b57b9d2142e89585b2 to your computer and use it in GitHub Desktop.
Realtime waveform display with tunable trigger level
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
__author__ = 'Scott H. Hawley' | |
__copyright__ = 'Scott H. Hawley' | |
__license__ = "MIT Licence (do what you want, don't blame me)" | |
import numpy as np | |
import cv2 | |
import soundcard as sc # https://github.com/bastibe/SoundCard | |
from scipy.ndimage.interpolation import shift | |
# screen size & related variables | |
imWidth, imHeight = 1024, 512 | |
y0 = imHeight/2 # horizontal axis | |
draw_amp = imHeight/2-1 # maximum amplitude in pixels | |
def draw_wave(screen, mono_audio, xs, title="oscilloscope", gain=5): | |
screen *= 0 # clear the screen | |
# next line determines the y-values for the wave form. - sign because CG is upside down | |
ys = ( y0 - draw_amp * np.clip( gain * mono_audio[0:len(xs)], -1, 1) ).astype(np.int) | |
pts = np.array(list(zip(xs,ys))) # pair up xs & ys | |
cv2.polylines(screen,[pts],False,(0,255,0)) # draw lines connecting the points | |
cv2.imshow(title, screen) # show what we've got | |
return | |
def find_trigger(mono_audio, thresh=0.02, pos_slope=True): # thresh = trigger level | |
start_ind = None # this is where in the buffer the trigger should go; None | |
shift_forward = shift(mono_audio, 1, cval=0) | |
if pos_slope: | |
inds = np.where(np.logical_and(mono_audio >= thresh, shift_forward <= thresh)) | |
else: | |
inds = np.where(np.logical_and(mono_audio <= thresh, shift_forward >= thresh)) | |
if (len(inds[0]) > 0): | |
start_ind = inds[0][0] | |
return start_ind | |
# main routine; audio buffer & sample rate | |
def oscilloscope(buf_size=1024, fs=44100): | |
default_mic = sc.default_microphone() | |
print("oscilloscope: listening on ",default_mic) | |
gain, trig_level = 5, 0.02 # seems to work on my mac | |
# allocate storage for 'screen' | |
screen = np.zeros((imHeight,imWidth,3), dtype=np.uint8) # 3=color channels | |
xs = np.arange(imWidth).astype(np.int) # x values of pixels | |
while (1): # keep looping until someone stops this | |
with default_mic.recorder(samplerate=fs) as mic: | |
audio_data = mic.record(numframes=buf_size) # get some audio from the mic | |
bgn = find_trigger(audio_data[:,0], thresh=trig_level) | |
if bgn is not None: # try to trigger | |
end = min(bgn+imWidth, buf_size) # don't go off the end of the buffer | |
pad_len = max(0, imWidth - (end-bgn) ) # might have to pad with zeros | |
draw_wave(screen, np.pad(audio_data[bgn:end,0],(0,pad_len), \ | |
'constant',constant_values=0), xs, gain=gain) # draw left channel | |
else: | |
draw_wave(screen, audio_data[0:imWidth,0]*0, xs, gain=gain) # draw zero line | |
key = cv2.waitKey(1) & 0xFF # keyboard input | |
if ord('q') == key: # quit key | |
break | |
elif ord('=') == key: # = sign (couldn't get arrow keys to register!) | |
trig_level += 0.002 | |
print("trig_level =",trig_level) | |
elif ord("'") == key: # single quote | |
trig_level -= 0.002 | |
print("trig_level =",trig_level) | |
elif ord('[') == key: # left bracket | |
gain *= 0.9 | |
print("gain =",gain) | |
elif ord(']') == key: # right bracket | |
gain += 1.1 | |
print("gain =",gain) | |
return | |
if __name__ == '__main__': | |
oscilloscope(buf_size=int(imWidth*1.5)) # give the buffer a little extra room so you don't see zero-pads |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment