I tried different ways to play sine waves in sounddevice, and they worked fine, until I tried to overlay multiple frequencies at once. I also get loud scratching noises in my speaker whenever there are no frequencies to play. I've simplified my code to this:
import sounddevice
import numpy
SAMPLE_RATE = 44100
frequencies = {440: 0, 550: 0, 660: 0}
# hz: start_index
def callback(outdata: numpy.ndarray, frames: int, time, status) -> None:
"""writes sound output to 'outdata' from sound_queue."""
# params may need annotations... :/
result = None
for frequency, start_index in frequencies.items():
t = (start_index + numpy.arange(frames)) / SAMPLE_RATE
t = t.reshape(-1, 1)
wave = numpy.sin(2 * numpy.pi * frequency * t)
if result is None:
result = wave
else:
result += wave
frequencies[frequency] += frames
if result is None:
result = numpy.arange(frames) / SAMPLE_RATE
result = result.reshape(-1, 1)
outdata[:] = result
stream = sounddevice.OutputStream(channels=1, samplerate=SAMPLE_RATE, callback=callback)
stream.start()
while True:
pass
I found that this code works if you make two changes to it.
The first change is to this while loop:
This loop will use nearly 100% of CPU, leaving little time for the callback function to run.
I changed this to repeatedly sleep instead:
This reduces CPU usage.
I also increased the number of frames requested at once.
I changed this line:
into this:
I did this because I found that the
framesparameter to your callback was 15. (This is on Mac, might be different for you.) I found that requesting one second's worth of audio made numpy able to more efficiently generate it, and prevented a weird glitch noise.After I do that, I get a dial-tone noise. (I assume that's what you were going for.)