I'm generating an exponential sweep with the following function:
@jit(nopython=True)
def generate_exponential_sweep(time_in_seconds, sr):
time_in_samples = time_in_seconds * sr
exponential_sweep = np.zeros(time_in_samples, dtype=np.double)
for n in range(time_in_samples):
t = n / sr
exponential_sweep[n] = np.sin(
(2.0 * np.pi * starting_frequency * sweep_duration)
/ np.log(ending_frequency / starting_frequency)
* (np.exp((t / sweep_duration) * np.log(ending_frequency / starting_frequency)) - 1.0))
number_of_samples = 50
exponential_sweep[-number_of_samples:] = fade(exponential_sweep[-number_of_samples:], 1, 0)
return exponential_sweep
Right now the sine wave does not finish at a zero-crossing, so for avoiding the problem I managed to make a fade function that simply fades the volume to zero:
@jit(nopython=True)
def fade(data, gain_start,
gain_end):
gain = gain_start
delta = (gain_end - gain_start) / (len(data) - 1)
for i in range(len(data)):
data[i] = data[i] * gain
gain = gain + delta
return data
The question is:
Since time_in_seconds
, sr
, starting_frequency
and ending_frequency
are all unknown, we can't guarantee that it will hit any zeroes or even cross it, without any giving them any constraints. The only way to properly do this is to use a window (or fade in/out), with a known frequency behaviour.
This rules out 1. We can continue with 2.
I would suggest a tapered cosine window for this task - scipy.signal.windows.tukey - which offers the fade-in/-out from 0 to 1 and vv, and is a very common choice for audio tasks.
An example of this can be implemented as -
import numpy as np
import scipy
def fade(data: np.ndarray, fade_time: float, sr: float) -> np.ndarray:
alpha = sr * 2 * fade_time / len(data)
window = scipy.signal.windows.tukey(len(data), alpha)
return data * window
The resulting window - with a fade time of 0.1 s - would look like this
To add this to your already existing code and simplying it -
import numpy as np
def generate_exponential_sweep(
time_in_seconds: float, sr: float, starting_frequency: float,
ending_frequency: float, fade_time: float) -> np.ndarray:
t = np.arange(0, time_in_seconds, 1/sr)
exponential_sweep = np.sin(2 * np.pi * (
starting_frequency * time_in_seconds * (
(ending_frequency / starting_frequency) ** (t / time_in_seconds) - 1
) / np.log(starting_frequency / ending_frequency)
)
)
exponential_sweep = fade(exponential_sweep, fade_time, sr)
return exponential_sweep
We can replace that whole block creating the sweep by scipy.signal.chirp which does exactly the same -
import numpy as np
import scipy
def generate_exponential_sweep(
time_in_seconds: float, sr: float, starting_frequency: float,
ending_frequency: float, fade_time: float) -> np.ndarray:
t = np.arange(0, time_in_seconds, 1/sr)
exponential_sweep = scipy.signal.chirp(
t, f0=starting_frequency, f1=ending_frequency,
t1=time_in_seconds, method='logarithmic')
exponential_sweep = fade(exponential_sweep, fade_time, sr)
return exponential_sweep
And just a general comment - don't mix putting variables as arguments and not. Please include all in
def generate_exponential_sweep(time_in_seconds, sr, starting_frequency, ending_frequency):
...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.