For the past two weeks or so, I've been working on constructing the Taylorator. The Taylorator is a piece of software which allows me to flood the FM broadcast band with Taylor Swift's music. No matter where you tune your radio, you will only be able to listen to her!
Okay, I admit that you could technically use the Taylorator to broadcast whatever music you want, so maybe it's a bit of a misnomer. But for some reason I figured this would be funnier.
What do I mean by flooding the FM broadcast band? Well, in Canada and the US (and maybe other places too), the FM broadcast band spans 88 MHz - 108 MHz. You can't broadcast wherever, though. Stations will only appear on odd-numbered frequencies, like 88.1 MHz, 94.5 MHz, 107.3 MHz, etc. There's a technical reason for this - every FM broadcast takes up about 150 KHz of bandwidth, and spacing the broadcasts like this allows for an extra 50 KHz of wiggle room.
This also works out to 100 different frequencies that we need to populate (with 100 different songs). So, how can we accomplish this?
Software Defined Radio
SDR, or Software Defined Radio, is a paradigm where you do most of your signal processing in software, and then a relatively dumb piece of hardware creates a real-world signal from this virtual signal. It works similarly to a sound card. It takes in a series of samples, and spits out a waveform that matches these samples.
They make SDRs that can transmit, receive, or do both. For this project we don't care about receive, and only need to be able to transmit. I chose to use a LimeSDR mini, because it can transmit, has a wide enough bandwidth, and I already had it lying around.
One important difference between a sound card and an SDR is that a sound card takes real-valued samples, and an SDR takes complex-valued samples. That is to say, each SDR sample can be presented as a single number a + bi
, where a
and b
are real numbers. This is primarily done because it cuts the required sample rate in half, as it allows for negative frequencies. On the hardware side, this results in a simpler design, which lowers cost.
Audio preparation
There are a few things we need to do to our raw audio to prepare it for modulation. First of all, we want the sample rate of all of our different songs to be the same. I chose 44.1 KHz as the target sample rate, but 48 KHz would also be a sane value (or anything else, really). I wrote a rational resampler which does this by upsampling, linear interpolating, and decimating to the target sample rate.
Next, we need to run our audio through a low-pass filter and perform FM-preemphasis. I won't touch on low-pass filtering much, but FM-preemphasis is basically just a high-pass filter. The idea is that random atmospheric noise, when demoulated by an FM receiver, is biased towards high frequencies. We can get around this by boosting our high frequencies on transmit (pre-emphasis) and having the receiver attenuate the same frequencies (de-emphasis).
This is all done before the modulation actually starts. Modulation takes a ton of CPU power so we want to pre-compute whatever we can.
FM modulation in software
FM modulation follows a pretty simple formula. Basically, y_n = e^(i*pi*sum(x))
, where y_n
is the output sample, and x
is the input audio stream up until this point. (Sorry, my blog doesn't support LaTeX! Maybe I should add that...) In other words, we're rotating around a circle, and the speed at which we rotate around this circle is dictated by the sum of all the audio samples we've seen up until this point. In the complex world, rotation speed is analogous to frequency, so this is all we need to build an FM modulator!
In practice, though, this is actually pretty difficult for our use-case. We need to modulate 100 audio streams at once. Each one needs to be offset by up to +/- 10 MHz, so we need to sample at 20MSPS. Performance was the name of the game, and I spent probably a week banging my head against my computer in order to get it to an acceptable level. Huge thanks to my friend Won, who gave me a ton of different ideas to try.
The current architecture modulates all audio channels at once, so we only need to write to our output array once (instead of reading/writing 100 times). The modulator is also responsible for "biasing" each channel to be centered around a different frequency. This used to be done as a separate stage but doing it as part of modulation sped things up significantly. Finally, rather than computing trig operations, a lookup table is generated which converts between phase angle and its complex number representation. This lookup table is also gain-compensated, so that when we add up the 100 outputs, we don't end up clipping.
I'm not convinced that I'm operating anywhere close to peak efficiency. There may be some huge DSP-specific shortcut that I'm overlooking - I'm certainly no expert. But the current code works well enough.
Performance
Due to the increase in required sample rate, as well as the increase in number of channels, required processing power grows at O(n^2), where n represents the amount of bandwidth we're covering.
On my laptop, which has a 10th-gen i5 CPU, I can only get to about 0.5x real-time performance if I target the entire FM broadcast band (88 MHz - 108 MHz). However, if I decrease that slightly to 88 - 104 MHz, then my laptop is able to handle it at slightly better than real-time.
My desktop, with a Ryzen 2700x CPU, fares better. Even only using a few cores, it can easily manage 2x real-time performance or more. This is a 7-year-old CPU, so the bar isn't actually too high here.
Memory usage is also pretty high. Since we're loading all songs into memory and pre-computing beforehand, it can take a few gigabytes to hold everything. With my music, I've noticed around 3.5 GB of RAM usage. On top of this, there's also per-thread RAM usage, though it should be less significant. There's certainly room for improvement here. Just changing how audio is stored could cut this usage in half, or more.
Legality
Whenever I told someone about the Taylorator during its development, the question I'd consistently get asked is, "is this legal?". This is going to depend on exactly where you live and exactly how you're using the Taylorator, but in general, I think the answer is... probably not.
There are generally cut-outs for very low power FM transmitters, like the ones people use in their car. Usually, though, this requires the transmitter itself to be licensed, and of course, the software I have written has no such license.
In practice, it's probably not a huge deal? A few mW spread over 20 MHz of bandwidth results in a pretty weak signal. Obviously, don't do illegal things (and if you do, don't tell me about it)! If you connected your SDR up to an amplifier you would almost certainly get in a bunch of trouble. So, uh, don't do that.
Source Code
As always, this project is open source!