Warm tip: This article is reproduced from serverfault.com, please click

Oscilloscope

发布于 2019-03-07 18:57:34

I am trying to create a program in python which can create an oscilloscope from live audio (through a microphone)

The difference to a normal oscilloscope is that this will only show one wave length, for example (desired output):

Output Example

This shows three different wavelengths, and how they will each show on the program.

My progress so far:

  1. I've created a program to show a graph and clear and redraw it
  2. I've then created a program which will display the sound live (Although it's very slow, which would ideally be fixed if possible)

Code #1:

import matplotlib.pyplot as plt
import time

plt.ion()

#y1 is the data
y1 = [0,0.309,0.587,0.809,0.951,1,0.951,0.809,0.587,0.309,0, -0.309, -0.587, -0.809, -0.951, -1, -0.951, -0.809, -0.587, -0.309, 0]

plt.plot(y1, 'r.-') #Graph with data
plt.plot([0 for _ in y1]) #Straight line at y=0

while True:
    #Update data to new data
    #y1 = new data
    plt.plot(y1, 'r.-') #Graph with data
    plt.plot([0 for _ in y1]) #Straight line at y=0
    plt.draw()
    plt.pause(0.5) #Time for one wave? Need some way to find this...
    plt.clf()

Code #2:

import pyaudio
import numpy as np
import matplotlib.pyplot as plt
import time

RATE = 44100
CHUNK = int(RATE/20) # RATE / number of updates per second

def soundplot(stream):
    t1=time.time()
    data = np.fromstring(stream.read(CHUNK),dtype=np.int16)
    plt.pause(0.1) #To draw graph!
    plt.clf()
    plt.plot(data)
    plt.draw()
    plt.axis([0,len(data),-2**16/2,2**16/2])
    print("took %.02f ms"%((time.time()-t1)*1000))

if __name__=="__main__":
    p=pyaudio.PyAudio()
    stream=p.open(format=pyaudio.paInt16,channels=1,rate=RATE,input=True,
                  frames_per_buffer=CHUNK)
    for i in range(int(20*RATE/CHUNK)): #do this for 10 seconds
        soundplot(stream)
    stream.stop_stream()
    stream.close()
    p.terminate()

Edit: To make it clear, my desired outcome is to show one single wave length as seen in the picture, instead of multiple which is what the second code produces

Questioner
Ruler Of The World
Viewed
11
J_H 2019-03-08 05:38:37

To show a single wavelength, first scan forward in time until you find a non-negative data point. (This would happen at once, with the very first entry, with the data in Code #1.)

Then continue scanning forward in time, and maintain a delta between successive samples. Initially the delta (or discrete derivative) will be positive as the curve approaches its max, then will turn negative till hitting a min, then will turn positive again.

Stop scanning forward in time when you encounter a non-negative data point and the delta is positive. At that point you have a full wavelength.

EDIT:

If you have lots of data then it's OK to skip some preamble data samples. The key here is we want to find a zero crossing with positive derivative, and then keep going until we find another zero crossing with positive derivative. So that first decision should both look for a non-negative data point and insist on positive delta.

In the presence of noise, we might see sign changes on delta more often than the period of the waveform. So an initial step might be to find min and max values for a bunch of samples (implying a range), then pick arbitrary thresholds like min + .25 * range and min + .75 * range, record the first positive zero crossing, wait for signal to exceed the high threshold, wait for it to go below the low threshold (after a negative zero crossing), then record the next positive zero crossing. That gives you an estimate of wavelength. Make repeated estimates if you find that helpful, and take some convenient aggregate like the mean or (better) the median.

Armed with a wavelength estimate, you're in a better position to evaluate whether a pair of positive zero crossings appear to be "correct" or due to noise. Reject pairs that are much closer together than your estimate would suggest. You may also find it convenient to compute a smoothed derivative, so instead of delta of last two points seen (K=2), you are averaging over the last K points, perhaps half a dozen of them. The average function is a low pass filter which rejects high frequency noise.