-2

How can I change the pitch of an imported signal logarithmically / exponentially over time?

Please note that the imported signals that will be used are not single frequencies so a simple sweep or a chirp command will not work since I will be importing vocal audio files, I just created the examples below so they would work and could be tested / show the issues I'm having.

I can change the pitch of a signal over time linearly which works great see part 1 of test code and frequency plot below. Thanks to Sheljohn for the code

%Sweep question part 1
clear all,clf reset,tic,clc 
pkg load signal  %load packages

%%%----create signal
start_freq=500;
end_freq=20;
fs=22050
len_of_sig=7; %in seconds
t=linspace(0,2*pi*len_of_sig,fs*len_of_sig);
orig_sig1=.8*sin(start_freq*t);
wavwrite([orig_sig1(:)] ,fs,16,strcat('/tmp/0_sig.wav'));  % export file

%%%---import signal
[ya, fs, nbitsraw] = wavread('/tmp/0_sig.wav');

orig_total_samples=length(ya); %make this the same length as signal wave
t_import=linspace(0,2*pi*(orig_total_samples/fs),orig_total_samples);

%%%%----Begin linsweep 
x = ya(:);
fac=(end_freq-start_freq)/length(x); %linear slope

n = numel(x); % number of timepoints
m = mean(x); % average of the signal
k = transpose(0:n-1); %

h = hilbert( x - m ); % analytic signal
env1 = abs(h); % envelope
sweep=fac*pi*k.^2/(fs); %linearly increasing offset original %alter curve here
p = angle(h) + sweep; % phase + linearly increasing offset original

y = m - imag(hilbert( env1 .* sin(p) )); % inverse-transform
wavwrite([y(:)] ,fs,16,strcat('/tmp/0_sweep.wav'));  % export file

%%%----------Used for plotting
z = hilbert(y);
instfreq = fs/(2*pi)*diff(unwrap(angle(z))); %orginal
t_new=t_import/(2*pi); %converts it to seconds

plot(t_new(2:end),instfreq,'-r')

xlabel('Time (secnds)')
ylabel('Frequency (Hz)')
grid on
title('Instantaneous Frequency')

Linear Plot

Issues with the code I have below are:

1) The frequency doesn't start or end at the correct frequency.

2) It doesn't have the correct slopes

I believe it has to do with the variables fac and sweep I'm just not sure how to calculate them correctly.

fac=log(start_freq/end_freq)/length(x); %slope
sweep=-(start_freq)*exp(fac*k); %alter curve here

-

%-----------------Sweep question part 2
clear all,clf reset,tic,clc 
pkg load signal  %load packages

%%%----create signal
start_freq=500;
end_freq=20;
fs=22050
len_of_sig=7; %in seconds
t=linspace(0,2*pi*len_of_sig,fs*len_of_sig);
orig_sig1=.8*sin(start_freq*t);
wavwrite([orig_sig1(:)] ,fs,16,strcat('/tmp/0_sig.wav'));  % export file

%%%---import signal
[ya, fs, nbitsraw] = wavread('/tmp/0_sig.wav');

orig_total_samples=length(ya); %make this the same length as signal wave
t_import=linspace(0,2*pi*(orig_total_samples/fs),orig_total_samples);

%%%%----Begin linsweep 
x = ya(:);
fac=log(start_freq/end_freq)/length(x); %slope

n = numel(x); % number of timepoints
m = mean(x); % average of the signal
k = transpose(0:n-1); %

h = hilbert( x - m ); % analytic signal
env1 = abs(h); % envelope
sweep=-(start_freq)*exp(fac*k); %alter curve here
p = angle(h) + sweep; % phase +  increasing offset

y = m - imag(hilbert( env1 .* sin(p) )); % inverse-transform
wavwrite([y(:)] ,fs,16,strcat('/tmp/0_sweep.wav'));  % export file

%%%----------Used for plotting
z = hilbert(y);
instfreq = fs/(2*pi)*diff(unwrap(angle(z))); %orginal
t_new=t_import/(2*pi); %converts it to seconds

plot(t_new(2:end),instfreq,'-r')

xlabel('Time (seconds)')
ylabel('Frequency (Hz)')
grid on
title('Instantaneous Frequency')

Incorrect frequency and slope

The slopes I'm trying to get are when the start frequency starts at 500hz and goes to 20hz. And when the start frequency starts at 20hz and it goes to 500hz. See plots below: Note: These frequency will change so I'm trying to get the correct formula / equation that will calculate these slopes when needed.

500hz to 20hz

20hz to 500hz

Ps: I'm using Octave 4.0 which is similar to Matlab.

Please note that the imported signals that will be used are not single frequencies so a simple sweep or a chirp command will not work since I will be importing vocal audio files, I just created the examples below so they would work and could be tested / show the issues I'm having.

Rick T
  • 3,125
  • 9
  • 43
  • 101
  • 1
    For anyone else looking at this question and using MATLAB, comment out or remove the line `pkg load signal %load packages` – informaton Jul 22 '17 at 16:37

1 Answers1

0

I can get the sweep to look like the plot you are interested in by making the following changes to your code. Some of them are just cosmetic for my sake (e.g. I like my time variables to remain in units of seconds throughout).

desired slope

Relevant changes:
From:

 t=linspace(0,2*pi*len_of_sig,fs*len_of_sig);
 orig_sig1=.8*sin(start_freq*t);
 fac=log(start_freq/end_freq)/length(x); %slope

To:

t=linspace(0,len_of_sig,fs*len_of_sig);
orig_sig1=0.8*sin(start_freq*t*2*pi);    
fac=log(end_freq/start_freq)/length(x);
sweep=(start_freq*2*pi/fs)*exp(fac*k); %alter curve here 

Here was some other changes I made,

y = env1.*sin(p);      
% and later for consistency
t_import=linspace(0,orig_total_samples/fs,orig_total_samples);
t_new=t_import; %t is seconds

The fac, in my mind, is going to be the difference from your start and end, so it would be: log(endFreq)-log(startFreq) or log(endFreq/startFreq) with the additional normalization for the length. This can be flipped with a negative sign in front.

One issue with the sweep may be happening when you use it to calculate p=angle(h)+sweep; where angle(h) is in radians.

The radians vs Hz units issue may be causing some of the difficulty.

Sardar Usama
  • 18,585
  • 9
  • 33
  • 54
informaton
  • 1,302
  • 2
  • 9
  • 18
  • 1
    Good discussion on chat. I'll leave the other answer up for a bit in case anyone else wants to continue from here and skip some of the initial confusion I had, but will delete it in another day or so since it was not what you were looking for. – informaton Jul 22 '17 at 20:16