larsks
larsks

Reputation: 312620

Generating smooth audio from Python on a low-powered computer

I am trying to write a simple audio function generator in Python, to be run on a Raspberry Pi (model 2). The code essentially does this:

For example:

import pyaudio
from numpy import linspace,sin,pi,int16

def note(freq, len, amp=1, rate=44100):
 t = linspace(0,len,len*rate)
 data = sin(2*pi*freq*t)*amp
 return data.astype(int16) # two byte integers

RATE = 44100
FREQ = 261.6

pa = pyaudio.PyAudio()
s = pa.open(output=True,
            channels=2,
            rate=RATE,
            format=pyaudio.paInt16,
            output_device_index=2)

# generate 1 second of sound
tone = note(FREQ, 1, amp=10000, rate=RATE)

# play it forever    
while True:
  s.write(tone)

The problem is that every iteration of the loop results in an audible "tick" in the audio, even when using an external USB sound card. Is there any way to avoid this, rather than trying to rewrite everything in C?

I tried using the pyaudio callback interface, but that actually sounded worse (like maybe my Pi was flatulent).

The generated audio needs to be short because it will ultimately be adjusted dynamically with an external control, and anything more than 1 second latency on control changes just feels awkward. Is there a better way to produce these signals from within Python code?

Upvotes: 2

Views: 3431

Answers (2)

2themaxx
2themaxx

Reputation: 31

I was looking for a similar question to yours, and found a variation that plays a pre-calculated length by concatenating a bunch of pre-calculated chunks.

http://milkandtang.com/blog/2013/02/16/making-noise-in-python/

Using a for loop with a 1-second pre-calculated chunk "play_tone" function seems to generate a smooth sounding output, but this is on a PC. If this doesn't work for you, it may be that the raspberry pi has a different back-end implementation that doesn't like successive writes.

Upvotes: 2

user149341
user149341

Reputation:

You're hearing a "tick" because there's a discontinuity in the audio you're sending. One second of 261.6 Hz contains 261.6 cycles, so you end up with about half a cycle left over at the end:

Waveform showing discontinuity at 1 second

You'll need to either change the frequency so that there are a whole number of cycles per second (e.g, 262 Hz), change the duration such that it's long enough for a whole number of cycles, or generate a new audio clip every second that starts in the right phase to fit where the last chunk left off.

Upvotes: 6

Related Questions