Simple Pygame Audio at a Frequency
Solution 1
What kind of 440 Hz sound? There are many types of waves at "440 Hz": sine, saw, square, etc. You could have a flute playing an A, and that might count too.
Assuming you want a sine wave, it looks like you can generate a Sound object with pygame.sndarray.samples
. (I haven't tested this) You can create the samples with:
samples = [math.sin(2.0 * math.pi * frequency * t / sample_rate) for t in xrange(0, duration_in_samples)]
This is hopefully basic sine wave stuff. frequency
is the desired frequency, in Hz. sample_rate
is the number of samples / second in the generated audio: 44100 Hz is a typical value. duration_in_samples
is the length of the audio. (5 * 44100 == 5 seconds, if your audio is at a 44100 Hz sample rate.)
It looks like you might have to convert samples
to a numpy.array
before passing to pygame.sndarray.samples
. The docs indicate that the audio should match the format returned by pygame.mixer.get_init
, so adjust samples
appropriately, but that's the basic idea. (mixer.get_init
will tell you the sample_rate
variable above, and whether you need to account for stereo, and if you need to adjust the amplitude of the wave or shift it.)
Make samples
a integral number of waves long, and it should loop.
Solution 2
After getting entirely too many "ValueError: Array depth must match number of mixer channels", and other similar errors, I discovered this working example http://www.mail-archive.com/[email protected]/msg16140.html. It properly generates a multi-dimensional 16 bit integer array that works with a stereo mixer. Minimum working example below, mostly lifted from the previous link with the necessary pygame bits thrown in. Tested in Python 2.7.2 with pygame.ver '1.9.1release'.
This example will play a 440 Hz tone out of one speaker and a 550 Hz tone out of the other speaker in a stereo setup. After a bit of playing with the duration I discovered that audible clicks will pop up in the sound loop if you set the "duration" variable to anything other than a whole number.
import pygame
from pygame.locals import *
import math
import numpy
size = (1366, 720)
bits = 16
#the number of channels specified here is NOT
#the channels talked about here http://www.pygame.org/docs/ref/mixer.html#pygame.mixer.get_num_channels
pygame.mixer.pre_init(44100, -bits, 2)
pygame.init()
_display_surf = pygame.display.set_mode(size, pygame.HWSURFACE | pygame.DOUBLEBUF)
duration = 1.0 # in seconds
#freqency for the left speaker
frequency_l = 440
#frequency for the right speaker
frequency_r = 550
#this sounds totally different coming out of a laptop versus coming out of headphones
sample_rate = 44100
n_samples = int(round(duration*sample_rate))
#setup our numpy array to handle 16 bit ints, which is what we set our mixer to expect with "bits" up above
buf = numpy.zeros((n_samples, 2), dtype = numpy.int16)
max_sample = 2**(bits - 1) - 1
for s in range(n_samples):
t = float(s)/sample_rate # time in seconds
#grab the x-coordinate of the sine wave at a given time, while constraining the sample to what our mixer is set to with "bits"
buf[s][0] = int(round(max_sample*math.sin(2*math.pi*frequency_l*t))) # left
buf[s][1] = int(round(max_sample*0.5*math.sin(2*math.pi*frequency_r*t))) # right
sound = pygame.sndarray.make_sound(buf)
#play once, then loop forever
sound.play(loops = -1)
#This will keep the sound playing forever, the quit event handling allows the pygame window to close without crashing
_running = True
while _running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
_running = False
break
pygame.quit()
Related videos on Youtube
Paul
General programmer. I have initiative, but I know less than I feel I ought to.
Updated on June 04, 2022Comments
-
Paul almost 2 years
How can I create a 440 Hz sound that plays smoothly forever using audio with Pygame? I assume this should be easy enough, but I don't want to use any stupid files to accomplish the task. The final intent of this is to play a note while a key is being held down as I have asked in another question. Any help would be greatly appreciated as I have wasted great quantities of time trying to find an answer to this.
-
Paul over 12 yearsHow can I make the sound play forever?
-
Thanatos over 12 yearsBy telling it to loop forever when you play it. Have you tried reading the docs? (pygame.org/docs) Specifically, you want pygame.org/docs/ref/mixer.html#Sound.play
-
David Stein almost 6 yearsThis is good - but it produces some weird behavior. Sometimes pygame plays both tones through both speakers simultaneously, but sometimes it plays a sequence of both tones (and an identical sequence through both speakers). Not sure what to make of that.
-
Love and peace - Joe Codeswell about 3 yearsThis worked just fine for me on windows 10 with python 3.9.