Your code assigns to pt
in callback
; Python determines the scope of a name at compile time and assignment makes this a local name.
pt = pt + frame_count
Unless you tell Python otherwise, that is. In Python 2, you can only mark a name explicitly as a global
instead, you need Python 3 to be able to use the nonlocal
keyword:
def callback(in_data, frame_count, time_info, status):
"""
This is the callback function for sound API
In each call, synthesized data is dumpped into the sound buffer
"""
nonlocal pt
wave = np.ndarray((frame_count, 2))
for i in range(frame_count):
ind = (i+pt) % n
wave[i,0] = float(x[ind]) * 2
wave[i,1] = float(y[ind]) * 2
pt = pt + frame_count
return (encode(wave), pyaudio.paContinue)
With the nonlocal pt
line Python is explicitly told not to treat pt
as a local name but to take it from the enclosing scope of get_audio_callback
instead.
In Python 2, you can just create a local that takes its value from the closure:
def callback(in_data, frame_count, time_info, status):
"""
This is the callback function for sound API
In each call, synthesized data is dumpped into the sound buffer
"""
pt_local = pt
wave = np.ndarray((frame_count, 2))
for i in range(frame_count):
ind = (i+pt_local) % n
wave[i,0] = float(x[ind]) * 2
wave[i,1] = float(y[ind]) * 2
pt_local = pt_local + frame_count
return (encode(wave), pyaudio.paContinue)
because your enclosing get_audio_callback
scope doesn't appear to use pt
anyway and won't need access to the updated pt_local
value.
If you do need pt
to update at the get_audio_callback
scope (if, say, callback
is called multiple times and you need pt
to be updated from call to call), you need to avoid using pt
as a local inside the callback
function altogether.
One effective work-around for that is to wrap the value in a mutable object or assign it as a mutable attribute somewhere that both the enclosing scope and the local scope can access it without it ever being seen as a local assignment. Setting an attribute on the callback
function is a good way to do that:
def get_audio_callback(pt):
def callback(in_data, frame_count, time_info, status):
"""
This is the callback function for sound API
In each call, synthesized data is dumpped into the sound buffer
"""
wave = np.ndarray((frame_count, 2))
for i in range(frame_count):
ind = (i+callback.pt) % n
wave[i,0] = float(x[ind]) * 2
wave[i,1] = float(y[ind]) * 2
callback.pt = callback.pt + frame_count
return (encode(wave), pyaudio.paContinue)
callback.pt = pt
return callback
Here callback.pt
is no longer a local name; it is an attribute on the callback
function object instead.