Python library for playing fixed-frequency sound
Solution 1
PyAudiere is a simple cross-platform solution for the problem:
>>> import audiere
>>> d = audiere.open_device()
>>> t = d.create_tone(17000) # 17 KHz
>>> t.play() # non-blocking call
>>> import time
>>> time.sleep(5)
>>> t.stop()
pyaudiere.org is gone. The site and binary installers for Python 2 (debian, windows) are available via the wayback machine e.g., here's source code pyaudiere-0.2.tar.gz
.
To support both Python 2 and 3 on Linux, Windows, OSX, pyaudio
module could be used instead:
#!/usr/bin/env python
"""Play a fixed frequency sound."""
from __future__ import division
import math
from pyaudio import PyAudio # sudo apt-get install python{,3}-pyaudio
try:
from itertools import izip
except ImportError: # Python 3
izip = zip
xrange = range
def sine_tone(frequency, duration, volume=1, sample_rate=22050):
n_samples = int(sample_rate * duration)
restframes = n_samples % sample_rate
p = PyAudio()
stream = p.open(format=p.get_format_from_width(1), # 8bit
channels=1, # mono
rate=sample_rate,
output=True)
s = lambda t: volume * math.sin(2 * math.pi * frequency * t / sample_rate)
samples = (int(s(t) * 0x7f + 0x80) for t in xrange(n_samples))
for buf in izip(*[samples]*sample_rate): # write several samples at a time
stream.write(bytes(bytearray(buf)))
# fill remainder of frameset with silence
stream.write(b'\x80' * restframes)
stream.stop_stream()
stream.close()
p.terminate()
Example:
sine_tone(
# see http://www.phy.mtu.edu/~suits/notefreqs.html
frequency=440.00, # Hz, waves per second A4
duration=3.21, # seconds to play sound
volume=.01, # 0..1 how loud it is
# see http://en.wikipedia.org/wiki/Bit_rate#Audio
sample_rate=22050 # number of samples per second
)
It is a modified (to support Python 3) version of this AskUbuntu answer.
Solution 2
The module winsound is included with Python, so there are no external libraries to install, and it should do what you want (and not much else).
import winsound
winsound.Beep(17000, 100)
It's very simple and easy, though is only available for Windows.
But:
A complete answer to this question should note that although this method will produce a sound, it will not deter mosquitoes. It's already been tested: see here and here
Solution 3
I'm putting my code in here as it helps a programmer gain clarity over how the code works. Explanation is in the code itself:
#!/usr/bin/env python3
import pyaudio
import struct
import math
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
p = pyaudio.PyAudio()
def data_for_freq(frequency: float, time: float = None):
"""get frames for a fixed frequency for a specified time or
number of frames, if frame_count is specified, the specified
time is ignored"""
frame_count = int(RATE * time)
remainder_frames = frame_count % RATE
wavedata = []
for i in range(frame_count):
a = RATE / frequency # number of frames per wave
b = i / a
# explanation for b
# considering one wave, what part of the wave should this be
# if we graph the sine wave in a
# displacement vs i graph for the particle
# where 0 is the beginning of the sine wave and
# 1 the end of the sine wave
# which part is "i" is denoted by b
# for clarity you might use
# though this is redundant since math.sin is a looping function
# b = b - int(b)
c = b * (2 * math.pi)
# explanation for c
# now we map b to between 0 and 2*math.PI
# since 0 - 2*PI, 2*PI - 4*PI, ...
# are the repeating domains of the sin wave (so the decimal values will
# also be mapped accordingly,
# and the integral values will be multiplied
# by 2*PI and since sin(n*2*PI) is zero where n is an integer)
d = math.sin(c) * 32767
e = int(d)
wavedata.append(e)
for i in range(remainder_frames):
wavedata.append(0)
number_of_bytes = str(len(wavedata))
wavedata = struct.pack(number_of_bytes + 'h', *wavedata)
return wavedata
def play(frequency: float, time: float):
"""
play a frequency for a fixed time!
"""
frames = data_for_freq(frequency, time)
stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, output=True)
stream.write(frames)
stream.stop_stream()
stream.close()
if __name__ == "__main__":
play(400, 1)
Solution 4
I streamlined jfs' answer for Python3.6+ and made some minor improvements:
import math
from pyaudio import PyAudio, paUInt8
def generate_sine_wave(frequency, duration, volume=0.2, sample_rate=22050):
''' Generate a tone at the given frequency.
Limited to unsigned 8-bit samples at a given sample_rate.
The sample rate should be at least double the frequency.
'''
if sample_rate < (frequency * 2):
print('Warning: sample_rate must be at least double the frequency '
f'to accurately represent it:\n sample_rate {sample_rate}'
f' ≯ {frequency*2} (frequency {frequency}*2)')
num_samples = int(sample_rate * duration)
rest_frames = num_samples % sample_rate
pa = PyAudio()
stream = pa.open(
format=paUInt8,
channels=1, # mono
rate=sample_rate,
output=True,
)
# make samples
s = lambda i: volume * math.sin(2 * math.pi * frequency * i / sample_rate)
samples = (int(s(i) * 0x7F + 0x80) for i in range(num_samples))
# write several samples at a time
for buf in zip( *([samples] * sample_rate) ):
stream.write(bytes(buf))
# fill remainder of frameset with silence
stream.write(b'\x80' * rest_frames)
stream.stop_stream()
stream.close()
pa.terminate()
generate_sine_wave(
# see http://www.phy.mtu.edu/~suits/notefreqs.html
frequency=523.25, # Hz, waves per second C6
duration=1.2, # seconds to play sound
volume=0.25, # 0..1 how loud it is
sample_rate=22050, # number of samples per second: 11025, 22050, 44100
)
![Adam Matan](https://i.stack.imgur.com/QG9pG.jpg?s=256&g=1)
Adam Matan
Team leader, developer, and public speaker. I build end-to-end apps using modern cloud infrastructure, especially serverless tools. My current position is R&D Manager at Corvid by Wix.com, a serverless platform for rapid web app generation. My CV and contact details are available on my Github README.
Updated on February 13, 2020Comments
-
Adam Matan over 4 years
I have a mosquito problem in my house. This wouldn't usually concern a programmers' community; However, I've seen some devices that claim to deter these nasty creatures by playing a 17Khz tone. I would like to do this using my laptop.
One method would be creating an MP3 with a a single, fixed-frequency tone (This can easily done by audacity), opening it with a python library and playing it repeatedly.
The second would be playing a sound using the computer built-in speaker. I'm looking for something similar to QBasic Sound:
SOUND 17000, 100
Is there a python library for that?
-
tom10 about 15 yearsBy the way, although this will produce a sound, I really doubt it will deter mosquitoes, in fact, I doubt they could even hear it. The issue is that most insects don't hear using tympanic membranes like we do, but hear using sensory hairs. But sensory hairs are only sensitive to air velocity, not pressure, and by the time you get far from the speaker, it's almost all pressure with very little velocity. That is, they won't hear it unless they are standing right on your speaker.
-
Adam Matan about 15 yearsSo the speaker wold have to be very powerful, and probably not a common PC speaker. Luckily, I am still a student in a science faculty - I will ask an entomologist and post the answer here.
-
Adam Matan about 15 yearsCool. Can you tell anything about stability issues? The lest release is 0.2.
-
jfs about 15 years@Udi Pasmon: PyAudiere is a simple wrapper for corresponding C++ library audiere.sourceforge.net
-
tom10 over 9 years"Whereas human ears are pressure detectors, a mosquito's detects the particle velocity component of a sound field, which is restricted to the immediate vicinity of the sound source in acoustic near field. The mosquito's ears are insensitive to pressure fluctuations in the acoustic far field." ncbi.nlm.nih.gov/pmc/articles/PMC1636503
-
Foxichu over 8 yearsThe fancy sample-writing code doesn't work for samples of durations <1s. You can replace
samples = (int(s(t) * 0x7f + 0x80) for t in xrange(n_samples)) for buf in izip(*[samples]*sample_rate): # write several samples at a time stream.write(bytes(bytearray(buf)))
withsamples = (int(s(t) * 0x7f + 0x80) for t in range(n_samples)) stream.write(bytes(bytearray(samples)))
-
natterstefan over 6 yearsA friend of mine shared this link with me to get and install pyaudio: people.csail.mit.edu/hubert/pyaudio
-
Regis May about 6 yearsIf you use this solution I get strange error messages sent to STDERR. How can I get rid of them? Please see stackoverflow.com/questions/50162431/… for details!
-
Tms91 over 4 yearsHow can I register winsound in Django? stackoverflow.com/questions/59047909/…
-
Steve Hageman over 2 yearsI tested this wonderful code with my FFT Analyzer and O-Scope and the code as written actually puts out twice the asked for frequency. This line:
c = b * (2 * math.pi)
, should be changed to:c = b * (1.0 * math.pi)
to put out the proper frequency. I would also like to add that the harmonics are better than -95 dBc when running a USB Sound Blaster Dongle - so this code generates a very good 16 bit quality Sine Wave. :-)