# How to Frequency Modulate an Audio Signal

I've started to learn about FM and I followed this guide to FM a sine wave.

I have managed to do it and here is the result:

And the code:

import numpy as np
import scipy.integrate as integrate
import matplotlib.pyplot as plt

BasebandFrequency = 10e3
CarrierFrequency = 100e3
SamplingFrequency = 1e7
BufferLength = 2000
modulation_index = 4
t = np.arange(0, 2000, 1 / BufferLength)
BasebandSignal = np.sin(2*np.pi*t / (SamplingFrequency/BasebandFrequency))
CarrierSignal = np.sin(2*np.pi*t / (SamplingFrequency/CarrierFrequency))

BasebandSignal_integral = -np.cos(2*np.pi*t / (SamplingFrequency/BasebandFrequency))

ModulatedSignal_FM = np.sin((2*np.pi*t / (SamplingFrequency/CarrierFrequency)) + (modulation_index * BasebandSignal_integral))

plt.plot(t, ModulatedSignal_FM)
plt.plot(t, BasebandSignal)
plt.show()


I tried following his other guide about FM an Audio file but I can't understand how to do it,

here is the audio file I'm using

This is what I came up with so far:

import matplotlib.pyplot as plt
import numpy as np
import scipy.integrate as integrate
import statistics

def generateSignalFM(time_vector,data):
TWO_PI = 2 * np.pi
fc = 80000
b = 25
fm = np.sin(TWO_PI * (fc + b * data) * time_vector)

return fm

def normalizeAudio(data):
return np.float32(data / max(data))

def averageAudio(data):
return np.float32(data / statistics.mean(data))

def main():
SAMPLE_FOR = 1 # in seconds

split_data = data[0:2000]
split_time = time_vector[0:2000]
audio_integrated = []
for i in range(2000):
audio_integrated.append(integrate.trapezoid(split_data[0:i]))
audio_integrated = averageAudio(audio_integrated)
audio_integrated = normalizeAudio(audio_integrated)
fm = generateSignalFM(split_time,audio_integrated)
plot_graph2d(split_time,split_data,split_time,fm)


And the result:

I am honestly lost, I managed to almost fully understand how to FM a sine wave but how do I do it with an Audio Signal?

• It'd help to attach sample data of interest (e.g. Drive, ufile, pastebin). Jul 25 at 17:25
• I added the audio file recording. @OverLordGoldDragon Jul 25 at 17:53
• Exactly the same way, you just use an audio signal instead of a sine wave? Jul 26 at 9:40
• It has been a while since I have done any FM/AM stuff, but I don't know see why it wouldn't work like it would for a sine wave. The best course of actions in this case would be to try and demodulate it and see if you get back the original audio signal. I think it should. Leave a comment here if it does not work which should give me and others something to work with. Also post your demodulation code. Jul 26 at 21:30
• The guide link seems to erroneously link to the first image file. Jul 27 at 8:03

### Algorithm

To encode a signal $$x_m$$ in a carrier with frequency $$f_c$$, we proceed as:

$$y(t) = \cos(\phi(t)), \\ \phi(t) = 2\pi \cdot \left(f_c t + f_\Delta \int_0^t x_m(\tau)d\tau \right)$$

where $$f_\Delta$$ controls the maximum deviation of $$y$$'s instantaneous frequency from $$f_c$$ (effectively its bandwidth, but not in strict Fourier sense).

Discrete-time implementation is as follows:

1. Integrate via cumulative sum
2. Ensure $$t$$ is sampled properly per sampling frequency, i.e. $$f_s = 1 / (t[1] - t[0])$$. In Python this means linspace(t_min, t_max, N / (t_max - t_min), endpoint=False)
3. Ensure $$\phi(t)$$ does not exceed $$\pi$$, adjusting $$f_\Delta$$ as necessary
4. Ensure $$f_c \leq f_s/2 - f_\Delta$$, where $$f_s$$ is sampling frequency.

### Applied example

Doing all of the above for the first 1 second of OP's attached data yields below, which is validated with direct inspection, and using synchrosqueezed CWT with an extremely time-localized wavelet:

Zooming (note, CWT is logscale, so it appears "stretched"):

Zooming even more, and showing the result:

### Code

Available at Github.

• Hey, thank you so much for the answer, it will take me some time to actually understand it so I might ask you some questions here, firstly what does data /= (2*np.abs(data).max()) do to the data? Normalize it? Jul 27 at 15:41
• @yarinCohen To ensure the values are between $[-.5, .5]$, so that $\cdot 2\pi$ is between $[-\pi, \pi]$ (as stated in code). $x$ is treated as instantaneous frequency, and integrating it gives phase, whose diff (in this case simply $x$) absolute value must be $<\pi$ to avoid aliasing. -- If my answers have been helpful, consider voting on them. Jul 28 at 13:36
• I'm sorry for the late question but why is fc arbitrary? I noticed when I modulate a 1 second of 44.1KHz with fc=2000 That looks good and accurate but if I modulate a 10 seconds of the same audio with fc=2000 it doesn't look accurate at all Aug 27 at 18:05
• @yarinCohen Not entirely arbitrary; the idea is, rapid frequency variations are better encoded at a higher mean frequency, fc - but if it's too high, we risk aliasing as modulations cross Nyquist. (It's "better" as in more accurate - in terms of audio quality I've little clue, but probably good idea to keep fc around input's mean frequency or below max) Aug 27 at 18:37
• Can you try modulating the same audio sound but 30 seconds of it? I can't find the best carrier frequency to use. At the moment I'm using 75KHz and it's not really accurate Aug 27 at 21:29