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.
Right click on the speaker icon in the taskbar. Select Playback Devices. Select Speakers and Properties. Go to Enhancements tab and uncheck Equalizer and Loudness Equalization.
With PyGame, you get two choices: Music or Sounds. Music will just play in the background when you call it to, and sounds will play at any time you call them to play.
Pygame can load WAV, MP3, or OGG files. The difference between these audio file formats is explained at http://invpy.com/formats. To play this sound, call the Sound object's play() method. If you want to immediately stop the Sound object from playing call the stop() method.
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()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With