1

I'm generating a sine wave and I want the second half of the signal to be in a different frequency. How do I find the phase shift I can apply to the second half so that the joining between the halves at a certain point in time is smooth?

More specifically, I'm generating sound and want to avoid the clicking sound when frequency changes.

Desired result joins smoothly (please ignore the poor image editing): enter image description here

time:
t0                                              t1                  

The result I currently has a gap at t1: enter image description here

And that happens because the 2nd part of the wave, when generated with the same phase (see it starts at 0° at t0), had a different Y axis value at t1: enter image description here

If you look back at the 1st image, the 2nd part had to be shifted a bit to the left (i.e. it's phase starts at something > 0°) in order to smoothly connect with the 1st part (i.e. to have the same Y axis value at the time of frequency change)

Victor Basso
  • 119
  • 3
  • well, do you know how to determine the phase of a sine? – Marcus Müller Jul 14 '21 at 23:20
  • 2
    (also, by the way, if you're generating both sines just before joining them: It might be easier to just modify the way you generate the sine at the point where you'd join them) – Marcus Müller Jul 14 '21 at 23:26
  • 1
    What are you thinking when you say join: matching only the phase, even if at the point of joining there will be a discontinuity, or ensuring there is as little discontinuity as possible? – a concerned citizen Jul 15 '21 at 15:19
  • @MarcusMüller I believe I'm trying to do as you say: "modify the way I generate at the point they join" and that modification is the phase shift I'm looking for. – Victor Basso Jul 15 '21 at 17:34
  • @aconcernedcitizen I mean ensuring there is no discontinuity, as in: the value of both halves of the wave have the same Y value at the join time. I tried to add images now to clarify. – Victor Basso Jul 15 '21 at 17:35
  • 1
    @Vituel Well, as you show it there is a discontinuity: at the point of joining the slopes are different. That's the first case I asked about, where you only consider the phase. The second case means you disconsider the phase in favour of the two waveforms having the same slope at that point. – a concerned citizen Jul 16 '21 at 05:31
  • @aconcernedcitizen ah right, so i misused "continuity", thanks for clarifying :) I'll update the question. – Victor Basso Jul 16 '21 at 08:57
  • Please see related answer here: https://dsp.stackexchange.com/questions/83009/when-concatenating-sine-waves-how-do-i-phase-shift-in-order-to-prevent-pops-c/83014#83014 – Dan Boschen May 14 '22 at 21:04

3 Answers3

2

Generate the second sine wave as

$$x(t) = sin(\omega_2 t + \omega_1t_0)$$

where $t_0$ is the time when the frequency changes from $\omega_1$ to $\omega_2$ .

Hilmar
  • 44,604
  • 1
  • 32
  • 63
1

To ensure smooth continuity we generate

$$ x_0 = \cos(\omega_0 t), \ t_0 \leq t < t_1 \\ x_1 = \cos(\omega_1 t + t_1 \cdot (\omega_0 / \omega_1)), \ t_1 \leq t < t_2 \\ $$

where $\omega_0 t$ is input phase for $x_0$.

Example

enter image description here

It cannot be perfectly smooth for any arbitrary $t_1$ per discretization limitations (but $t_1$ can be chosen to maximize smoothness; use e.g. derivative criteria).

Explanation

For double the frequency, the "same" point will occur at half the input phase, so to generate $f=2$ as continuous with $f=1$, we generate $f=1$ up to some cutoff point, and then $f=2$ starting from half the cutoff point. Generalize "double" and "half" via $f_0 / f_1$.

Code

import numpy as np
import matplotlib.pyplot as plt

N = 128 cut = 19 f0, f1 = 4, 8

t = np.linspace(0, 1, 128, 0) x0 = np.cos(2np.pi f0 * t)[:cut] x1 = np.cos(2np.pi f1 * (t + t[cut]*(f0/f1)))[:-cut] x = np.hstack([x0, x1])

plt.plot(x) plt.title("f0, f1 = %s, %s | transition at sample=%s" % (f0, f1, cut), weight='bold', loc='left', fontsize=16)

OverLordGoldDragon
  • 8,912
  • 5
  • 23
  • 74
0

Here is my picture.

Image of signals to be “merged"

You don't need to shift the sinusoid since in a programming context you just need the values to append in a buffer (or file).

We have a first sinusoid (orange) and let's just say it stopped at the cyan point (just a little before the full cycle). We need to pick up from there with the second sinusoid (red). The points where the value (height/vertical direction) of the second is the same as that last point of the first are the pink/magenta ones (there are two but we will just use one - if you need to dive into the fine details of which is better, you can).

Now the two sinusoids will be equal means

$$ \sin \left(2 \pi f_{1} t_{1} \right) = \sin \left(2 \pi f_{2} t_{2} \right) \tag{1} $$

By taking the $\arcsin$ we get

$$ 2 \pi f_{1} t_{1} = 2 \pi f_{2} t_{2} \tag{2} $$

(there is also $2 \pi f_{1} t_{1} = 2 \pi f_{2} t_{2} + \theta$ for the other point).

And so

$$ t_{2} = \frac{f_{1}}{f_{2}} t_{1} \tag{3} $$

$t_{1}$ is the time when the first sinusoid ended (cyan point). $t_{2}$ is the time when we need to start taking the values of the second sinusoid (magenta point).

So you just let the second sinusoid run until it reaches $t_{2}$; then you start taking its values and append to your buffer $\implies$ no click.

All these times are counted from $0$ - no need to do the math shifting of the sinusoid.

(The green sinusoid is just for illustration purposes - the red-shifted)

gpasch
  • 101
  • 1