3

I want to generate a sound via MATLAB which consists of a pre-defined number of different frequency-glides (sweeps). To this end I wrote a MATLAB code. However, I encountered two problems:

1) when I play the sound, the sound clicks throughout its entire duration.

-> This might be related to different phase angles at the end of the preceding and the beginning of the following sweep segment. I tried to solve the problem (see my code below) - so far unsuccessful. The spectrogram of such a sound you will see here:

spectrogram of concatenated sound which is perceived with several 'soft' clicks

2) when I generate the sound, sometimes there is a much more distinct click besides these softer ones. This is clearly visible in the spectrogram. --> Here I am not sure what the problem could be and how to avoid it.

spectrogram of concatenated sound with additional distinct click

The code how I generate the sounds is as follows:

clear all;
close all;

%% define stimulus parameters
soundDuration = 1200; % duration of sound
sf = 44100; % sampling rate
ampl = 0.05; % 0.05; % ampl
segmentDuration = 25; % duration of one standard segment in ms
nSegments = soundDuration/segmentDuration; % number of segments of which the sound should consist of
t = 0:1/sf:(0.025-1/sf); % time vector for segment

%% generate sound consisting of n sweep-segments
complexSound = [];
for iSeg = 1:nSegments

    f1 = 1000:10:3000; 
    f1 = randsample(f1,1); % start freq in Hz for current sweep segment
    f2 = 1500:10:4500;
    f2 = randsample(f2,1); % end freq in Hz  for current sweep segment

    if iSeg == 1
        sweep = ampl * chirp(t,f1,segmentDuration/1000,f2,'logarithmic'); % generate sweep-segment withou considering the phase

    else
        sweep = ampl * chirp(t+1/sf,f1,segmentDuration/1000,f2,'logarithmic',ph); % the current sweep starts with a t+1/sf later and with the phase angle with which the previous sweep ended

    end
    ph = -90+360*(f2*t(end)+1/sf); % calculate the phase at the time point at which the current sweep ends and from that calculate the starting phase for the next sweep

    sweep = sweep';
    complexSound = [complexSound; sweep]; % concatenate sweep segments to form the complex sound

end
stim = complexSound; 
sound(stim,sf);

I appreciate any help on solving these problems.

anne w
  • 75
  • 6
  • I figured out the problem 2) which only sometimes occurs. The logarithmic version of the chirp function only works when f1 and f2 are different. Thus, one can easily constraint this via a code. – anne w Sep 24 '16 at 17:38

2 Answers2

1

so, basically what you want is frequency modulation. well, not really, but we can basically abuse the function matlab provides for it. the trick goes like this:

  1. we create one vector with as many elements as the final signal. each element of the vector holds the frequency the final signal should have at that specific sample.
  2. we use the fmmod function to create a 1Hz signal that gets modulated by the vector we created in 1.

i tried to adapt your code and keep it as similar as possible. however, all durations are in seconds now.

and please note that i assume that edges between the individual segments are ok. so, if you have one segment ending at, for instance, 1500Hz and the next start at 2000Hz, there is no smooth progression between these two. maybe the fmmod function smoothes that out a bit, but i did not check.

however, here is the code:

%% clear
clear all;
close all;

%% define stimulus parameters
soundDuration = 1.2; % duration of sound in seconds
sf = 44100; % sampling rate
ampl = 0.05; % 0.05; % ampl
segmentDuration = 0.025; % duration of one standard segment in s
nSegments = round(soundDuration/segmentDuration); % number of segments of which the sound should consist of
samples_per_segment = floor(sf * segmentDuration);

%% generate sound consisting of n sweep-segments
modulator = zeros(1, nSegments * samples_per_segment);
first_idx = 1;
for iSeg = 1:nSegments

    f1 = 1000:10:3000; 
    f1 = randsample(f1,1); % start freq in Hz for current sweep segment
    f2 = 1500:10:4500;
    f2 = randsample(f2,1); % end freq in Hz  for current sweep segment

    modulator(first_idx:first_idx + samples_per_segment-1) = logspace(log10(f1), log10(f2), samples_per_segment); % we add the logarithmic progression from f1 to f2 here
    first_idx = first_idx + samples_per_segment;

end
%% create final sound
stim = fmmod(modulator, 1, sf, 1); % here we abuse the frequency modulation function of matlab.
                                   % we basically tell it to create a 1Hz
                                   % sinewave and modulate it by the vector
                                   % we created in the previous step. the
                                   % last argument tells it basically that
                                   % if i give it a 1, it should increase
                                   % the frequency by 1Hz, if i give it a
                                   % 100, it should increase the freq by
                                   % 100Hz and so on...
stim = ampl .* stim; % multiply by the amplitude

%% plot...
spectrogram(stim,1000,[],[],sf,'yaxis'); % this provides a nice tf plot....

%% play
sound(stim,sf);
thht
  • 51
  • 5
  • thanks a lot. this does excactly what I requested, though I now realize that 25-ms frequency-glide segments in combination with the start- and end frequencies are probably too short to avoid the clicking. by using longer durations for the frequency-glide segments the code perfectly produces concatenated sounds without clicking. – anne w Sep 22 '16 at 20:03
0

I received another solution for this problem from Dik J. Hermes, which I will share as well. The advantage here is that one does not rely on the fmmod function (required for the solution above) which is available only in a commercial toolbox.

The problem in the original code (at the top of this page) was that the administration of the phase was not correct. The code below first calculates the instantaneous frequency, insFreq(t). If stim(t) = a*sin(phi(t)), then instFreq = phi'(t)/2*pi, or phi(t) = 2*pi*integral(instFreq). The instFreq(t) is calculated in the for loop. After the for loop phi(t) is calculated by integrating instFreq and multiplication by 2*pi. In this way one takes care that the signal stim itself is continuous and does not contain phase jumps. Due to the abrupt change of instantaneous frequency, the time derivative of stim is not continuous at the transitions from one segment to another. This will give cause to some small rudimentary clicks, which cannot be avoided (at least not with the parameters used here (f1, f2, segment duration).

clear all;
close all;

% define stimulus parameters
soundDuration = 1.2; % duration of sound
sf = 44100; % sampling rate
dt = 1/sf;
ampl = 0.05; % 0.05; % ampl
segmentDuration = 0.025; % duration of one segment in s
nSegments = round(soundDuration/segmentDuration); % number of segments of which the sound should consist of
tSegment = 0:dt:(segmentDuration-dt); % time vector for segment

% generate sound consisting of n sweep-segments
complexSound = [];
logInstFreq = [];
startFreqArray = 1000:10:3000;
endFreqArray = 1500:10:4500;
p = tSegment/tSegment(end);
for iSeg = 1:nSegments
    f1 = randsample(startFreqArray,1); % start freq in Hz for current sweep segment
    f2 = randsample(endFreqArray,1); % end freq in Hz  for current sweep segment
    logf1 = log10(f1);
    logf2 = log10(f2);
    logInstFreq = [logInstFreq (logf2-logf1)*p+logf1]; 
end
instFreq = 10.^logInstFreq;
phi = 2*pi*cumsum(instFreq)*dt;
stim = ampl*sin(phi); 
t = 0:dt:(length(instFreq)-1)*dt;
plot(t, instFreq)
sound(stim,sf);
audiowrite('sound.wav', stim, sf);
anne w
  • 75
  • 6