Pyglet Has a Synth

It never occurred to me that an OpenGL library like Pyglet would have a synth and many waveforms in it

24Mar26

Roar. Bears, I've been working on a small April Fool's Day project. I'll save what it is for the day, but the most surprising thing I found while working on that is this. The library I used, Pyglet, has a simple synth built in. If you're not familiar with the term, "synth" is short for synthesiser, a piece of instrument that takes in a note value like "D4" and plays a sound of that note. For a long time I had thought the simplest synth I could use was Scratch, due to how poorly I can play a musical instrument. This has obviously changed.

Before we can talk about how to make a sound, we need to know how Pyglet structures the sound playing classes and methods. In Pyglet, the class that controls the playback of sounds is a Player. Each sound is represented by a Source. Right now the example in the docs only shows a file as the source, but as we will see later, we can use synthesised sources. When you want to play a sound, you "queue" a source to the player. You then call play() and other methods on the Player to do things you normally would to a music player.

In the pyglet.media.synthesis package, there is a SynthesisSource, which takes a generator that makes waveforms in numbers between -1.0 and 1.0. In the package, there are generators for sine wave, sawtooth, triangle, pulse and noise, each wrapped into their own source. There's even a silence generator in case you need some gap in the music.

The sine wave source Sine requires a frequency, not a note like "D4". How do I get the number for the frequency of a D4? You can calculate it yourself using the frequency of A4, which is 440Hz, and the logarithm rule. But I used another library I've used many times, Librosa, for this.

import librosa
from pyglet.media import Player
from pyglet.media.synthesis import Sine

player = Player()
d4_freq = librosa.note_to_hz("D4")
source = Sine(duration=1, frequency=freq)
player.queue(source)
player.play()

I said the synth was simple because at the moment I can't find a way to make it play a chord, or multiple notes together. Right now the composite_operator in the package seems to do that, but 1) I haven't tried it yet, and 2) the operator seems to work only vertically across parts, but not horizontally along each part. This can introduce challenges in timing, since generally, parts in choral music don't move all at the same time.

At this club, we mostly use Pygame rather than Pyglet for Python projects, so we can't use Pyglet's synth. There are two things we can do about this. We can either look for a generic Python synth, or we can look at the source code of Pyglet's synth and make something similar using other libraries. A sine wave is easy to generate. We just need to write that waveform to the soundcard on the computer somehow. I used to use PyAudio to read input from a microphone and write sound to play to a speaker.

With this, what would the April Fool joke be about? You'll have to wait and see.

  • Home
  • Diary