56 lines
1.5 KiB
Python
Executable file
56 lines
1.5 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
import pyvisa
|
|
import time
|
|
|
|
import pyaudio as pa
|
|
import numpy as np
|
|
|
|
phase = 0.0 # in cycles
|
|
scaled_frequency = 0.0
|
|
# actual frequency = scaled_frequency * sample_rate
|
|
# For repeatability, scaled_frequency should ideally fit in
|
|
# much fewer than 53 bits.
|
|
# More precisely, if scaled_frequency * frame_count is always exact, we get
|
|
# negligible phase errors over 1hr.
|
|
|
|
def audio_callback(in_data, frame_count, time_info, status):
|
|
global phase
|
|
L = np.sin(2 * np.pi * (phase + np.arange(frame_count) * scaled_frequency)).astype(np.float32) # cast to float
|
|
delta_phase = scaled_frequency * frame_count # exact
|
|
delta_phase -= np.floor(delta_phase) # exact
|
|
phase += delta_phase # error at most ulp(1) = 2^-52
|
|
if(phase >= 1):
|
|
phase -= int(phase)
|
|
outbytes = L.tobytes()
|
|
#print(L, frame_count)
|
|
return (outbytes, pa.paContinue)
|
|
|
|
paudio = pa.PyAudio()
|
|
output_strm = paudio.open(format=pa.paFloat32,
|
|
channels=1,
|
|
rate=48000,
|
|
output=True,
|
|
stream_callback=audio_callback)
|
|
output_strm.start_stream()
|
|
|
|
input_strm = paudio.open(format=pa.paFloat32,
|
|
channels=1,
|
|
rate=48000,
|
|
input=True)
|
|
input_strm.stop_stream()
|
|
|
|
def do_frequency(scaled_f):
|
|
scaled_frequency = scaled_f
|
|
time.sleep(0.2) # Stall for latency/transitory behavior
|
|
input_strm.start_stream()
|
|
L = input_strm.read(int(48000 * 0.1)) # We get 1/sqrt(n) noise rejection and 1/n spurious tone rejection
|
|
input_strm.stop_stream()
|
|
return np.frombuffer(L, dtype=np.float32)
|
|
|
|
import matplotlib.pyplot as plt
|
|
L = do_frequency(440.0/48000)
|
|
print(L)
|
|
plt.plot(L)
|
|
plt.show()
|
|
|