Skip to content

Queuing very short sounds creates stuttering (694) #531

Open
@GalacticEmperor1

Description

@GalacticEmperor1

Issue №694 opened by l0rb at 2018-12-25 22:59:33

I'm playing a sound on a channel and repeatedly queue it whenever the queue is empty. if the sound becomes very short, there is very audible stuttering of the sound. it happens with lengths that fall below about 0.1s.
here is how my code approximately looks:

#  data = a numpy array with nice sound
sound = pygame.sndarray.make_sound(data)
channel = sound.play()
while True:
    if not channel.get_queue():
        #  if this would work, the goal is to create a new sound object with different data here
        channel.queue(sound)

Related Docs:
https://www.pygame.org/docs/ref/mixer.html
https://www.pygame.org/docs/ref/sndarray.html


Comments

# # e1000 commented at 2018-12-30 16:58:20

I think it could be related to # 322 , so can you try if this bug disappear if you change the mixer buffering, eg with

pygame.mixer.pre_init(buffer=2048)

at the beginning of your code, cf [pygame doc](https://www.pygame.org/docs/ref/mixer.html# pygame.mixer.pre_init)


# # l0rb commented at 2019-01-01 21:38:33

I tried again with decreased buffer size which does help, but does not cure the problem fully. I am now able to queue sounds with a length of about 0.01s but anything below and the stuttering reappears. (0.01s = about 500 frames at a sample rate of 44100)


# # e1000 commented at 2019-01-02 13:54:55

I might be wrong, but it seemed to me that SDL doesn't check any new input during the duration corresponding to the buffer?
so for very small sound snippets, maybe we should be considering to use pygame.midi?

Which buffer size did you use for your 10 ms sounds?


# # illume commented at 2019-01-02 13:57:45

I can't reproduce since that code is not complete.

Have you tried buffer=512? This should work on most systems. Maybe even 256 is ok for your application.

The other issue with repeated sounds is if they do not match up on their boundary. Like if you have a sine wave repeating, and because of numeric inaccuracy the border of the sound does not match up. This is why when making synths they usually repeat the sound multiple times until there is a match on the border. Another way to avoid the problem is to use 32bit samples (but you can only use those in pygame 2).

Additionally, I would try pygame with SDL2. But you have to compile from source currently. It has a rewritten mixer, and is better in many respects. See https://www.pygame.org/wiki/Compilation for instructions (for each platform there is a sdl2 specific instruction).


# # l0rb commented at 2019-01-04 12:04:08

I created a minimum working sample that has the buggy behavior so it can easily be reproduced. I made sure that the sounds do match up at the boundary, and I experimented with different buffer sizes but going very low (below 128) doesn't seem to change anything.

import math
import pygame
import numpy as np

#  this is the (approximate) sample duration in seconds
#  putting 0.01 here works fine, putting 0.005 or lower creates very audible stuttering and sometimes no sound at all (numbers might be hardware dependent?)
sample_duration = 0.005

buffer = 128

freq = 44100
pygame.mixer.pre_init(frequency=freq, size=8, channels=1, buffer=buffer)
pygame.init()
pygame.mixer.init()

#  creating a sine wave of `cycle_length` frames and append it `repeat` times to get close to `sample_duration` in duration of the sound
cycle_length = 200
repeat = max(1, round(freq * sample_duration / cycle_length))
frame_rad = 2*math.pi / float(cycle_length)
data = np.array([math.sin(frame_rad * frame) for frame in range(cycle_length)])
data = np.resize(data, cycle_length*repeat)
data = (data * 127).astype('i1')

piece = pygame.sndarray.make_sound(data)
channel = piece.play()
while True:
    if not channel.get_queue():
        channel.queue(piece)

# # ankith26 commented at 2021-11-23 03:22:26

Testing whether this issue still reproduces on pygame 2, so I ran the test code and with pygame 2+, I get the following error

Traceback (most recent call last):
  File "E:\test.py", line 21, in <module>
    piece = pygame.sndarray.make_sound(data)
  File "C:\Users\Ankith\AppData\Local\Programs\Python\Python310\lib\site-packages\pygame\sndarray.py", line 88, in make_sound
    return mixer.Sound(array=array)
ValueError: Array must be 2-dimensional for stereo mixer

Is this a regression? clearly the number of mixer channels is set to 1?

Metadata

Metadata

Assignees

No one assigned

    Labels

    mixerpygame.mixersndarraypygame.sndarray

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions