Search code examples
matlabaudioclickphase

How to generate a sound that consists of different frequency-glide-segments via MATLAB?


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.


Solution

  • 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);