I want to play a continuous sine wave (for now) which changes its frequency (for now) according to the mouse pointer position (for now). So, I generate and play a wave of the length of one frame (1/2s in the code below). The resulting sound has small gaps in the sound, enough to be very notable. Increasing the duration of the individual sounds does not help, changing the buffer size does not help, using dtick instead of dt does not help.
Is there a way to achieve a continuous sound this way? Or any other way?
Thank you
Versions: Python 3.9.3, pygame 2.0.1
Minimal (still lengthy) code:
import numpy as np
import pygame
from pygame import Color
from pygame.locals import KEYDOWN, KMOD_CTRL, QUIT, K_q
fade_out = np.linspace(1, 0, 100)
fade_in = np.linspace(0, 1, 100)
bits = 16
sample_rate = 44100
max_sample = 2 ** (bits - 1) - 1
def gen_sound(duration, freq):
n_samples = int(duration / 1000 * sample_rate)
buf = np.zeros((n_samples, 2), dtype=np.int16)
buf[:, 0] = (
0.25
* max_sample
* np.sin(2 * np.pi * freq[0] * np.arange(n_samples) / sample_rate)
)
buf[:, 1] = (
0.25
* max_sample
* np.sin(2 * np.pi * freq[1] * np.arange(n_samples) / sample_rate)
)
buf[-100:, 0] = (buf[-100:, 0] * fade_out).astype(int)
buf[-100:, 1] = (buf[-100:, 1] * fade_out).astype(int)
buf[:100, 0] = (buf[:100, 0] * fade_in).astype(int)
buf[:100, 1] = (buf[:100, 1] * fade_in).astype(int)
return pygame.sndarray.make_sound(buf)
def main(argv: list = None):
# initialize sound
pygame.mixer.pre_init(sample_rate, -bits, 2, 512)
# initialize pygame
pygame.init()
size = (1000, 800)
surf = pygame.display.set_mode(size)
surf.fill(Color("black"))
clock = pygame.time.Clock()
# frames per second and duration of a frame in ms
FPS = 2
dt = 1000 / FPS
dtick = 1000 / FPS
# position of the mouse pointer, None means outside of the window
mpos = None
# event loop
while True:
events = pygame.event.get()
for e in events:
if e.type == QUIT:
return
# shutdown with C-q
if e.type == KEYDOWN:
if e.mod & KMOD_CTRL and e.key == K_q:
return
if pygame.mouse.get_focused():
mpos = pygame.mouse.get_pos()
else:
mpos = None
freq = mpos if mpos else (440, 440)
sound = gen_sound(dt, freq)
sound.play(loops=0, fade_ms=0)
pygame.display.update()
dtick = clock.tick(FPS)
if __name__ == "__main__":
main()