If the parameter "frames" of the function callback is less than the samples in the current sound, for example, frames = 50 and the samples in the current sound = 70, there is a bug can cause an audible glitch.
First callback (starting fresh, self.current_sound_frame=0)
current_sound_frame_local = 0, available_frames = 70
Copies sound_data[0:50] into ret[0:50]
Updates self.current_sound_frame to 50
Returns 50 samples (indices 0..49)
Second callback (self.current_sound_frame=50)
current_sound_frame_local = 50, available_frames = 20
Iteration 1: copies sound_data[50:70] → ret[0:20], self.current_sound_frame = 70
Iteration 2: due to a bug, current_sound_frame_local is not recomputed, so it copies sound_data[50:70] again → ret[20:40], self.current_sound_frame = 90
Iteration 3: copies sound_data[50:60] → ret[40:50], self.current_sound_frame = 100
Returned ret is [50..69, 50..69, 50..59] (duplicates the tail instead of wrapping or zero-filling)
If the parameter "frames" of the function callback is less than the samples in the current sound, for example, frames = 50 and the samples in the current sound = 70, there is a bug can cause an audible glitch.
First callback (starting fresh, self.current_sound_frame=0)
current_sound_frame_local = 0, available_frames = 70
Copies sound_data[0:50] into ret[0:50]
Updates self.current_sound_frame to 50
Returns 50 samples (indices 0..49)
Second callback (self.current_sound_frame=50)
current_sound_frame_local = 50, available_frames = 20
Iteration 1: copies sound_data[50:70] → ret[0:20], self.current_sound_frame = 70
Iteration 2: due to a bug, current_sound_frame_local is not recomputed, so it copies sound_data[50:70] again → ret[20:40], self.current_sound_frame = 90
Iteration 3: copies sound_data[50:60] → ret[40:50], self.current_sound_frame = 100
Returned ret is [50..69, 50..69, 50..59] (duplicates the tail instead of wrapping or zero-filling)