Play a tone interactively with 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 environment called
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 Anaconda prompt, and macOS or linux users should open a terminal or terminal emulator (I recommend iTerm2 for Mac users). Then activate the
crackedbassoon 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 simpleaudio, Qt, 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 following command:
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 something different—perhaps
[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 preferable over
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 problems (see here).
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.