Playing a tone interactively using Python
This is the second post in a series about using Python to generate and play sounds.
Here, I describe how to play a pure tone interactively from the terminal. This is super
basic stuff. As I wrote in the first post, I
want to go slowly and not to assume any prior knowledge of either Python or sound
synthesis. I do assume, however, that you read and followed the recommendations in my
first post. In particular, I assume that you installed conda and created a new conda
crackedbassoon. If you didn’t do those things, the code examples below
might not work exactly as described.
Before running any of these commands, make sure that the volume on your computer is set very low, especially if you are using headphones!
Windows users should open their
and macOS or linux users should open a terminal or terminal emulator (I recommend
iTerm2 for Mac users). Then activate the
environment using the following command:
conda update conda conda activate crackedbassoon
Presumably you already installed NumPy into this environment. (If not, type
conda install numpy). Now we are going to install another third-party package called
sounddevice. This package
provides bindings for PortAudio, a cross-platform audio
library, and a few convenience functions to play and record NumPy arrays. We will use
sounddevice to deliver NumPy arrays to the computer’s audio output. There are numerous
Python packages with similar functionality, such as
and pygame. I have experimented extensively with all of
these packages on different computers and operating systems, and for numerous reasons
that I won’t go into here, sounddevice is the one I recommend for most users unless you
are also using the features of another package (e.g., you are writing a Qt app). Install
sounddevice using the
pip install sounddevice
Python interactive session
In the same window, simply type
This will launch a Python interactive session. Take a look at the three or four lines
that appear just above the Python prompt (
>>>). In my case, they were
(crackedbassoon) Samuels-MacBook-Air:~ smathias$ python Python 3.7.2 (default, Dec 29 2018, 00:00:04) [Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin Type "help", "copyright", "credits" or "license" for more information.
These lines contain the words
crackedbassoon, so I know I’m running the
correct Python distribution within the correct environment. If in your case it says
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.0.42)] on darwin, for instance—the
rest of the commands in this post might not work correctly because the particular
distribution or environment might not contain the required packages.
Third-party Python packages are not immediately accessible. Type the following lines into your terminal (which is now running an interactive Python session) to import the two packages we need:
import numpy as np import sounddevice as sd
There are several ways to use the
import statement. It can import whole packages
import pkg as pk, or
from pkg import *), subpackages modules, or
specific objects (
from pkg.subpkg.mod import func). The configuration
import pkg as pk imports the package
pkg into the namespace
pk. This is
import pkg, which imports into the longer namespace, because it
saves keystrokes in the long run. It is also preferable over
from pkg import *,
which imports the package into the current namespace, potentially causing numerous
Generate a pure tone
Pure tones are arguably the simplest sounds that exist. They are also used as building blocks in the synthesis of other sounds. I’ll talk more about pure tones in the future posts. For now, we’ll simply create one of them with a single line of code.
tone = np.sin(2 * np.pi * 440 * np.arange(0, 1, 1/44100))
Don’t worry too much about what this line actually does right now—I’ll explain everything thoroughly in the future.
Play the tone
Sounddevice makes it extremely easy to play NumPy arrays:
Upon execution of that last line, you should hear a tone. You should also notice that
as soon you as execute this line, it returns a fresh prompt (
>>>). This is because
playback was aysnchronous, meaning that Python continued to process further commands
while the tone continued to play in the background. You can block Python from performing
any other actions until the sound has finished playing like this:
sd.play(tone, 44100); sd.wait()
This second time, you should hear the same tone but the prompt will not appear until the sound is over.
The meanings of the functions
sd.wait() are obvious from their names.
It is also clear that the first argument we passed to
sd.play() was the NumPy array we
wished to play. But what is the meaning of the second argument we passed to this
function, the integer
44100? It is the sample rate, how many times per second the
waveform is evaluated. We will talk about sample rates again in the next post.
As a script
Below is a script that will play the same tone when executed.
"""Simply generates a pure tone using numpy and plays it via sounddevice. As always, make sure your volume settings are low before running this script, especially if you are using headphones! """ import numpy as np import sounddevice as sd if __name__ == "__main__": tone = np.sin(2 * np.pi * 440 * np.arange(0, 1, 1 / 44100)) # generate the tone sd.play(tone, 44100) # play it sd.wait() # wait for the tone to finish
We’ve played our first sound using Python. As I wrote at the beginning, this was basic stuff. In the next post, we will go a tiny bit deeper by considering the pure tone in more detail.
- Originally posted March 17, 2019.
- Added links to third post on March 24, 2019.
- Changed the repo on August 02, 2019.