DEV Community

Sam Pagenkopf
Sam Pagenkopf

Posted on

An FM Synth for Web (Part 2: Audio)

Demo Fazeoid here

What is Fazeoid?

I designed Fazeoid. Neonfuz and I implemented it in Svelte + Typescript.

Fazeoid is a fun web-based synthesizer. In part 1, I describe the technologies used to make Fazeoid. In this post, I talk about details of the synthesizer itself.

Fazeoid is a combination of an FM synthesizer with multiple Additive synthesizers (and I plan on adding filters, so it could also be subtractive). It uses a modulation matrix to create any possible pattern of frequency modulation, giving the user thousands of possible sounds.

The synth is integrated to give live feedback (both audio and visual), giving a high potential for creative freedom.

FM Synthesis

If you've ever used a Sega Genesis or a DOS computer, you may recognize the sweet sounds of FM synthesis. These game soundtracks are using FM synths almost entirely, except for any drums or voice samples.

If you want to hear the wide variety that FM is capable of, the channel GSTmix has a whole playlist of mixes. There are also many present day artists that use FM synths in their chiptune, as a step above the bleepy sounds of the NES and others. For example Savaged Regime and the artists of Ubiktune.

The synths used in old computers are actually PM synthesis, but at first I will call all of it FM. This class of synthesis actually dates much further back, since it is inherently simple, yet yields rich and varied results. Though, the 80s and onward is when it found its most common usage.

The FM synth has a distinct and harmonically rich sound that can't be compared to any other. It is not a single sound, but rather an entire class of sounds based on the special relationship between two or more oscillators.

Some people learn to recreate real sounds with FM synths, while others embrace the other-worldly potential of this funny oscillator. Some musicians have used it to create beautiful sound, while others have battered the FM synth into ear-splitting, tinny game soundtracks.

What actually is FM?

There are many ways to grasp FM synthesis, conceptually. Intuitively speaking, imagine that you have a constant tone. Now, imagine that you wobble the pitch of the tone up and down, kind of like a singer. Now, imagine that you increase the speed of that wobbling until the character of the tone actually changes. This is the essence of FM synthesis. The reason it is called "FM" is because the pitch or frequency of the audible tone is being modulated.

Another way to understand is that the amplitude of one wave is being used to alter the phase of another. Imagine the function sine(sine(time)). Don't even imagine it, look at it here, and adjust the slider.. See the complex pattern made by the output wave? This is the characteristic of an FM synth, responsible for its complex wave.

A third way to understand is that an FM carrier is much like a waveshaper. A waveshaper is an operation that changes the shape of a wave, for example rounding off the peaks of a wave, or stretching the wave out. Basically, each point on a wave is run through an operation F that transforms it into another point. To do nothing, F would be F(x) = x. To invert a wave, F would be F(x) = -x. In the case of an FM synth, F(x) = sine(x). So if you imagine X is a low number between 0 and PI/2, the rise of the sine wave will be very similar to a straight line, and it will not alter the input sound very much. But, when the amplitude of X exceeds PI/2, the wave will begin to wrap around, bringing the peaks of the wave back downwards towards amplitude 0. And of course, eventually it will wrap back up again, too.

Now, extend this thinking beyond sine waves. FM can refer to any type of wave modulating any other type of wave, each with distinct characteristics.

In the following explanations, I will use "Modulator" to refer to any wave which is used to modulate another wave, called the "Carrier", a wave which carries the signal of the other wave in its frequency or phase. This is similar to the carrier wave used in FM radio, where the amplitude of the radio signal translates to the frequency of the radio wave within its band. Just remember, Modulator => Carrier.

FM vs PM synthesis

Many people inaccurately describe PM synthesis as "FM", but they aren't the same. PM is Phase Modulation, and FM is Frequency Modulation. I initially wanted Fazeoid to be a phase modulation synthesizer, hence the name, but I accidentally did frequency modulation, and stuck with it.

Mathematically speaking, phase is the integral of frequency, and frequency is the derivative of phase. The way to translate between FM and PM is to take the derivative of integral of the modulator wave. With a sine wave, this does almost nothing, changing only the phase of the wave. Human ears are very bad at hearing constant phase differences, so for 2OP sine-based PM or FM oscillators, things are about the same. But the moment the modulator strays from a pure tone, things change. The difference between PM and FM is reflected in the tonal characteristics of the modulator wave. Taking the derivative of any given sound wave makes the wave sound much more harsh or tinny, and this is reflected in the "tight" or "tinny" sound of PM compared to the full-bodied sound of FM. Integrating a wave makes it more muffled, of course. Though one must be careful when integrating, since any DC bias (wherein the average amplitude of the wave strays from 0) will accumulate and cause the result to go far off the rails. This is why I center all of my waves at 0.

From a computer's viewpoint, PM is an P=A operation, where FM is an P=P+A operation. In the first case, the amplitude of the modulator is used to set the phase of the carrier. In the second case, it is used to add to its phase.

I believe the reason why old computers used Phase Modulation is because they could utilize look-up tables to simplify this operation in their sound chips, for example using logarithms to turn the multiply into an add. The operation of many of these old chips is fully documented and recreated. Since sine and multiply are pretty trivial operations today, I went whole hog and used FM. I suspect that there is an efficient way to create FM synths as well as PM synths on an 8-bit computer chip, and maybe an acute reader can correct me and point out a vintage chip, or a potential algorithm for doing so. It is possible that the real reason for this difference is based on avoiding patent infringement, or it is based on the tinnier sound of PM being better suited for tiny computer and TV speakers.

FM Pitch ratio

The full formula for a phase modulated pair is actually sin(sin(time * pitch0) * depth + time * pitch1)). Each oscillator has its own pitch, which is simply the rate at which the phase is incremented in Hertz, or cycles per second. This is also a very important part of their sound.

One design problem that still affects fazeoid today is that FM oscillators sound best when they have integer pitch ratios to each other, resulting in more uniform waves. Right now, the pitch knobs are set to behave as arbitrary values, which encourages users to put in sub-optimal values. I will at some point implement a new UI to facilitate this.

Image description

The FM Matrix

As you have likely observed, Fazeoid is not a two-oscillator (or 2OP) synthesizer. It has 4 oscillators right now, but it will soon be expanded to an arbitrary number of oscillators. Each oscillator can feed forward its modulation into any subsequent oscillator. This means that the first oscillator is only a modulator, the last is only a carrier, and all in-between oscillators can act both as carriers and modulators. Don't think in nouns here, but in verbs. An oscillator is a carrier or modulator insofar as it takes in modulation (carrier) or puts out modulation (modulator).

Connecting several modulators in parallel to one carrier (a horizontal line on the matrix) increases the potential of that carrier in a fairly linear way. The phase effects from every modulator is summed to create the phase effect on the carrier.

Connecting FM oscillators in series (a diagonal line on the matrix) exponentially increases the complexity of the output wave. The modulation isn't summed together, but rather each oscillator transforms the signal one more time.

I have considered adding self-modulation (aka feedback) and reverse modulation to Fazeoid, but I stray away from it because I'm not sure how to implement them while keeping the synth independent of sample rate. These types of modulation can have very chaotic results. In analog terms, self-modulation is like adding a tiny delay to the oscillator amplitude, and then feeding that value back into its pitch. On old sound chips, that delay would be one sample. But on a modern computer, the synth can demand an arbitrary sample rate, and in order to create a consistent sound I would have to decide on a fixed feedback delay interval, and implement an allpass filter in order to delay accurately. Even then, it may not have consistent results.

Additive Synthesis

Of course, I wasn't content with a sine-only FM synthesizer. But, I thought about the problem. The synths of yore used phase modulation with look-up tables, so it was easy for them to alter how the sine table was sampled in order to create new wave forms. When I implemented these same type of waves, there was a very annoying issue: aliasing aka foldover distortion noticeable in the higher frequencies. This is the audio equivalent of crusty pixel aliasing seen in video games. It's called "foldover distortion", because in the process of sampling a wave, any frequency which is above half of the sampling frequency will "fold over" and create inverted harmonics that cause nasty artifacts. Anything up to half of the sampling frequency, according to the Nyquist theorem, can be correctly represented, and anything above it causes artifacts.

This is when I came up with the idea of using additive waves to recreate the waves used in old-fashioned FM sound chips, meaning that instead of just having a simple sine function for each oscillator, I will generate many many sines for each one. Now, my initial idea was actually using these equations to recreate the spectrum of the fully modulated wave, but I can't read these formulas and I have no idea how they would be extended to a whole matrix of oscillators, and especially not a matrix with varying wave types.

So to avoid this aliasing issue, my additive synth generates the sounds in a loop, but refuses to generate anything above 1/4th of the sampling frequency. Why 1/4th and not 1/2? Well, when a high frequency wave is used as modulator, it can cause the carrier to alias. I found that this was a good compromise to reduce those artifacts, but there is likely a better way I have yet to find. If you have any insight, leave a comment on this issue. My code only knows immediate pitch by the velocity of the phase after the sum of all modulation, and by cutting out high frequency waves adaptively it has the potential to create new artifacts -- it's probably not the best solution, but what is? Also, as an optimization, I have a limit to the number of sines generated which will roll of the pitch of higher waves. This creates a softer sound than what could be, but I like the result of the compromise.

So, in order to know which sines to generate, I set about finding formulas to describe which set of sines, when added together, will recreate my desired sounds. AKA, finding the discrete fourier transforms for these waves. For the first few waves, this was easy -- I just found someone else who did it. But eventually, I wanted to create more waves, and I was having trouble. Nobody online, no Wolfram Alpha, no ChatGPT could help me out in creating the fourier transforms needed. I tried to do the math, but my math is too rusty. So, I solved the problem in code, of course. By taking the integral of sin(pitch * x) * f(x), you can determine the amplitude of a particular frequency of wave f(x). So, I generated my wave, multiplied it by sine and cosine at every potential frequency, and took a riemann sum of each. The amplitude of sine and cosine are used to create a vector in the imaginary plane, of length equal to the target amplitude, and angle equal to the target phase. I used this technique to find the amplitude and phase of the component waves for each target wave, and then I put the data into excel, did a curve fit, and used that to create my harmonic series. I intentionally chose 6 waves that have different harmonic profiles, as to make the synth as interesting as possible to use. Let me know if you want the code.

Future plans

Overall, the future plans for Fazeoid are here. Comment on these issues if you have any idea what I should do next.

Subtractive synthesis

Fazeoid is not yet subtractive. I probably need to crack open a DSP textbook and possibly calculus in order to really proceed how I intend to. I want to create resonant filters that can be envelope controlled, for example. The approach of cutting out waves during the additive synthesis step to emulate a filter is also possible, but I'm weary of the fact that this could multiply artifacts, rather than smooth them out.

Conclusion

Fazeoid has been a fun adventure in synthesis land and in web frontend land. It's a space I love to be in, full of practical math, engineering, and with immediate and fun results.

But, I want to pour my effort into things that will be rewarded. If you appreciate this project, please let me know however you can. If it goes ignored and unappreciated, it will never develop any further.

Top comments (0)