I have as input audio tones at various frequencies for varying lengths of time. I'm trying to slice those inputs into chunks (of the same size regardless of input frequency), and reproduce the chunks clearly in the output. When I use the blocking method, e.g. stream.write(), I get a clean output but there isn't much processing power left over to handle other tasks. So I've converted the code to a non-blocking approach with the callback() function, as shown below.
I've tried to pattern my code after this post: https://stackoverflow.com/a/22354593/12076263 but that does not work at all.
# Generate a 250 Hz tone in chunks amounting to 100 chunks per second,
# using a non-blocking method.
#
# How does one fit a 250 Hz wave into 100 Hz packets?
# Slice up a sine wave into segments of two-and-a-half waves apiece:
#
# | | | |
# _ _ _| _ _ | _ _ _| _ _ |
# / \ / \ / \ / \ / \ / \ / \ / \ / \ / \
# \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
# | | | |
# <----X1----> | <----X2----> | <----X1----> | <----X2----> |
#
# Play back each segment in alternating fashion.
# This simulates continuous audio where zero crossings do not necessarily
# occur at the edge of a chunk.
import numpy as np
import pyaudio
import time
# based on examples from https://people.csail.mit.edu/hubert/pyaudio/docs/
def callback(in_data, frame_count, time_info, status):
waveData = Y.tobytes()
return (waveData, pyaudio.paContinue)
Y = np.zeros((441), dtype = np.int16) # initialize audio array Y
p = pyaudio.PyAudio()
stream = p.open(format = 8, # 8 is code for int16 format
channels = 1,
rate = 44100,
frames_per_buffer = 441,
stream_callback = callback, # !! COMMENT OUT to enable blocking
output = True)
stream.start_stream()
X1 = np.linspace(0, 5 * np.pi, num = 441, endpoint = False)
Y1 = (5000 * np.sin(X1)).astype(np.int16) # Generate two-and-a-half waves
X2 = np.linspace(5 * np.pi, 10 * np.pi, num = 441, endpoint = False)
Y2 = (5000 * np.sin(X2)).astype(np.int16) # The "other" two-and-a-half waves
# Play the two wave segments one after the other
# Result should be a clean 250 Hz tone
while True:
Y = Y1 # First set of waves
time.sleep(0.0015) # This 1.5 millisecond delay
# simulates other stuff happening
# within the While loop.
#stream.write(Y.tobytes()) # !! UNCOMMENT to use blocking method
Y = Y2 # Second set of waves
time.sleep(0.0015) # More miscellaneous delays
#stream.write(Y.tobytes()) # !! UNCOMMENT to use blocking method
Expected a distortion-free (as digital audio goes) sine wave, and got a heavily distorted sound, like the roar of a giant movie monster. There are normally no error messages, but if the time.sleep values are increased, underruns might occur. Also the problem goes away if the time.sleep statements are removed entirely, but the full application in which this will be used will have at least a 1.5 millisecond processing load per each chunk.