# How to compute a complex modulated signal from an audio file?

I have a 30-seconds wav audio file with a sample rate of 44100Hz, obviously this array of samples is a 1D array, and so when I modulate it in AM (Amplitude Modulation) I get back a 1D array, but I want it to return a 2D array of complex values, I don't really know how can you generate the real and imaginary part from one value and so I ended up here.

Here is my code:

SAMPLE_FOR = 30 # in seconds
data = data[0:int(samplerate*SAMPLE_FOR)]
fm = generateSignalFM(data,time)

def generateSignalAM(slc,t):
samples,time_vec = SamplerateConversion(slc)
w = low_cut_filter(samples,176400,22050)

TWO_PI = 2 * np.pi
carrier_hz = 20000
ac = 0.5

carrier = np.sin(TWO_PI * carrier_hz * time_vec)
envelope = (1.0 + ac * w)
modulated = envelope * carrier
return modulated


And FM signal code:

def generateSignalFM(samples,t, fc=None, b=.3):

w = low_cut_filter(samples,fs,22050)

N = len(w)
if fc is None:
fc = 75000

x0 = w[:N]
# ensure it's [-.5, .5] so diff(phi) is b*[-pi, pi]
x0 /= (2*np.abs(x0).max())

# generate phase
phi0 = 2*np.pi * fc * time_vec
phi1 = 2*np.pi * b * np.cumsum(x0)
phi = phi0 + phi1
diffmax  = np.abs(np.diff(phi)).max()
# b correction
if diffmax >= np.pi:
diffmax0 = np.abs(np.diff(phi0)).max()
diffmax1 = np.abs(np.diff(phi1)).max()
phi1 *= ((np.pi - diffmax0) / diffmax1)
phi = phi0 + phi1

# modulate
x = np.cos(phi)
return x


If the signal is a real audio waveform that only has AM components, the baseband equivalent waveform will be real unless the carrier has a phase offset. If the intention is to model the effect of carrier phase offset, this can be accomplished by multiplying the baseband waveform with $$e^{j\phi}$$ for a given carrier offset $$\phi$$ resulting in a complex signal.

If the intention is to make an analytic signal (positive half spectrum only), the relationship is given by:

$$X(\omega) \Leftrightarrow x(t) = I(t)+jQ(t)$$

Where $$x(t)$$ is the complex analytic signal, $$I(t)$$ is the original real waveform, and $$Q(t)$$ is the Hilbert Transform of $$I(t)$$. $$X(\omega$$) is the Fourier Transform of $$x(t)$$ and will have positive frequency components only.

The analytic waveform can be created directly with the OP's code by changing the carrier from a real carrier as given by

carrier = np.sin(TWO_PI * carrier_hz * time_vec)


To a complex analytic signal carrier as given by:

carrier = np.exp(1j * TWO_PI * carrier_hz * time_vec)


This is because $$e^{j\omega t}$$ is the analytic signal for $$\cos(\omega t)$$. Compare the following given by Euler's equation to the general relationship previously given above for the real signal $$I(t)$$ and its analytic signal $$x(t)$$:

$$e^{j\omega t} = \cos(\omega t) + j\sin(\omega t)$$

Here, $$e^{j\omega t}$$ is the analytic signal, $$\cos(\omega t)$$ is the original real carrier, and $$\sin(\omega t)$$ is the Hilbert transform of $$\cos(\omega t)$$.

• Thank you, this did work for me and the code addition was very useful to understand, however I was trying to change my FM function to compute complex numbers as well and I don't understand how to use your solution there. do you mind taking a look? I added it to my post Commented Sep 3, 2021 at 23:22
• @yarinCohen yes also very simple- instead of using $\cos(\phi)$ which is a real function, use $e^{j\phi)$ Commented Sep 4, 2021 at 0:40
• So I change: x = np.cos(phi) To: x = np.exp(1j / phi) ? Commented Sep 4, 2021 at 1:48
• is it normal that I get divide by zero errors and not a number errors back? Commented Sep 4, 2021 at 11:10
• That is not a division symbol but a mathjax code to print the symbol phi. For your python code you would use np.exp(1j * phi). Commented Sep 4, 2021 at 11:23