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:

enter image description here

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)

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

    time_vector, data, sample_rate = readAudioFile('Recording.wav',SAMPLE_FOR,5)
    split_data = data[0:2000]
    split_time = time_vector[0:2000]
    audio_integrated = []
    for i in range(2000):
    audio_integrated = averageAudio(audio_integrated)
    audio_integrated = normalizeAudio(audio_integrated)
    fm = generateSignalFM(split_time,audio_integrated)

And the result:

enter image description here

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?

  • $\begingroup$ It'd help to attach sample data of interest (e.g. Drive, ufile, pastebin). $\endgroup$ Jul 25 at 17:25
  • $\begingroup$ I added the audio file recording. @OverLordGoldDragon $\endgroup$ Jul 25 at 17:53
  • $\begingroup$ Exactly the same way, you just use an audio signal instead of a sine wave? $\endgroup$
    – user253751
    Jul 26 at 9:40
  • 1
    $\begingroup$ 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. $\endgroup$
    – Paddy
    Jul 26 at 21:30
  • 1
    $\begingroup$ The guide link seems to erroneously link to the first image file. $\endgroup$ Jul 27 at 8:03


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:

enter image description here

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

enter image description here

Zooming even more, and showing the result:

enter image description here


Available at Github.

  • $\begingroup$ 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? $\endgroup$ Jul 27 at 15:41
  • $\begingroup$ @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. $\endgroup$ Jul 28 at 13:36
  • $\begingroup$ 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 $\endgroup$ Aug 27 at 18:05
  • 1
    $\begingroup$ @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) $\endgroup$ Aug 27 at 18:37
  • $\begingroup$ 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 $\endgroup$ Aug 27 at 21:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.