10
$\begingroup$

I'd like to synthesise a series of bat social-call like sounds (that look 'wavy' on the spectrogram, (see schematic below). I'm looking to test my acoustic analyses with synthetic audio , and being able to generate sounds with arbitrary frequency modulations is important.

enter image description here

I know how to make other synthetic sounds like pure tones, and various sweeps (linear, hyperbolic, log) using scipy.signal.chirp in python, and its analogs in MATLAB. Essentially, the input needs to be the frequency at a given time/sample, and the output needs to be a waveform that results in that 'frequency-profile'.

Can someone point me to a formula to generate arbitrary frequency modulated sounds, or even an open-source tool to generate such sounds?

$\endgroup$

3 Answers 3

7
$\begingroup$

If you consider that for a signal

s(t) = sin(phi(t))

the frequency is

f(t) = 1/(2pi) d/dt phi(t)

then given a desired frequency function f(t), you construct first a phase function

phi(t) = 2pi \int_0 ^t f(x) dx

which you use in your phasor: sin(x), cos(x), exp(ix)

Example:

f(t) = f0 + (f1-f0) * t # linear FM

phi(t) = 2pi * (f0 * t +(f1-f0) * t^2) = 2pi * (f0+(f1-f0)/2 * t) * t)

Note: the integration explains the factor 2 in (f1-f0)/2

Edit: I did a small Matlab script to visualize better what the answer is about.

    %signal generation
    %
    % sketch frequency
    u=[0.1,1000
    0.2,4000
    0.3,1500
    0.4,5000];
    %
    % interpolate
    fs=24000;
    T=(0:1/fs:0.5)';
    v=interp1(u(:,1),u(:,2),T,'pchip');
    %
    % truncate if necessary
    isel=(u(1,1)<=T) & (T<= u(end,1));
    v=v(isel);
    t=T(isel);
    %
    % integrate to obtain phase
    om=2*pi*cumtrapz(v)/fs;
    %
    % generate amplitude function (here attenuate initial/final 
    % transient
    aa=ones(size(t)); 
    i1=(1:0.1*fs)';
    aa(i1)=aa(i1).*exp(-1/2*((i1-i1(end))/(0.03*fs)).^2);
    i2=length(t)+1-fliplr(1:0.1*fs)';
    aa(i2)=aa(i2).*exp(-1/2*((i2-i2(1))/(0.03*fs)).^2);
    %
    % generate signal
    s=aa.*sin(om);
    %
    % for visualisation add leading and trailing zeroes
    s=[zeros(0.1*fs,1);s;zeros(0.1*fs,1)];
    %
    % for visualization add noise
    s=s+0.01*randn(size(s));
    %%
    %visualize
    figure(1)
    subplot(211)
    plot(u(:,1),u(:,2),'o',t,v)
    subplot(212)
    spectrogram(s,hann(256),128,1024,fs,'yaxis');
    line(t*1000,v/1000,'color','k')

which generates enter image description here

To address one of Dan Stowell concerns I added a amplitude weighting function that removes initial and final transients.

Edit: to understand if simulated signal shows unwanted effects, it is best to inspect spectrogram. I did this in example to detect and remove initial and final transients. Readers with access to Matlab are invited to experiment with script.

$\endgroup$
6
  • 2
    $\begingroup$ Please do not generate your own sinusiods using simple trig like this. It will very likely lead to problems such as aliasing, frequency/phase drift, etc. A particularly common problem is the lack of windowing (tapering) at the beginning/end of sinusoids, which can lead to broadband artefacts in both analysis experiments and experiments with animals. Use a well-designed software tool instead, as in @Noil's answer $\endgroup$
    – Dan Stowell
    Jul 22, 2022 at 15:50
  • $\begingroup$ I can NOT agree with Dan's comment on aliasing, frequency shift/phase drifts. I would advice to add an amplitude function that attenuates initial and final transients. It is really irrelevant how you synthesize your phasor, you always mast make sure that your transmitter is not saturating. $\endgroup$
    – WMXZ
    Jul 22, 2022 at 19:17
  • $\begingroup$ We can agree to disagree, that's fine. However, implementation is not irrelevant when it can lead to artefacts. I emphasise that I've seen these problems actually happen, both in published papers and live software. $\endgroup$
    – Dan Stowell
    Jul 23, 2022 at 10:43
  • 1
    $\begingroup$ I added Matlab code and picture to show the process $\endgroup$
    – WMXZ
    Jul 23, 2022 at 19:05
  • $\begingroup$ Would be enough to check the spectrogram to make sure the simulation does not generate unexpected artefacts? $\endgroup$ Jul 27, 2022 at 14:02
7
$\begingroup$

An alternative would be to draw the pitch over time —as you did it in your example picture— and generate the associated sound. This can be straight-forward sometimes, depending of your constraints.

You can do this easily if you are familiar with Max or Pure Data programming languages (draw the pitch, select the speed it should be read, link the drawing to the frequency of the sinusoidal generator).

You could also use a more-friendly-interface software such as Spear; it says "Features include: pencil tool for drawing":

enter image description here (picture from Klingbeil 2005)

$\endgroup$
1
  • 1
    $\begingroup$ This is a great answer. As well as Max & Pure Data, I would recommend to add SuperCollider (my favourite). I would expect there to be a good Python library too, but no - there are a few options but none of them very mature/dependable $\endgroup$
    – Dan Stowell
    Jul 22, 2022 at 15:57
3
$\begingroup$

Edited 2022-8-9: the bug is fixed in the new release published yesterday August 8, 2022, version 39.

Edited 2022-7-27: a bug discovered after posting this answer prevents a newly created whistle to not be stored properly in the persistent database and therefore the new whistle frequencies are lost when the app is closed; the bug is getting fixed and in the mean time please use the editor while taking this into consideration; a new version will be released when the bug is fixed.

Original post: Here's an open source tool to generate such sounds, the source code is in gitlab: DC Dolphin Communicator, free for Android, from https://play.google.com/store/apps/details?id=sm.app.dc - I wrote it. It may do what you need, although the app is also designed for whistle recognition. It is best used on a tablet instead of a phone.

It has an editor that lets you build whistles within a ~0.5 kHz to 11 kHz range. You need to defined the frequency values in Hz at each 40th of a second. You give the whistle a name. The app can emit the whistle by entering the name in the Emit entry field in the main screen; it will interpolate between the given frequency values when it generates the voltage samples to emit (voltage sampling is 48000 per second for most tablets). Your whistle will be kept in a database which will be reloaded the next time you use the app and which can be exported to a file that can be sent remotely.

To use the editor in DC and emit a new whistle:

  1. open the app (the first time, you need to allow a few permissions required by Android);
  2. select the WHISTLES top menu item;
  3. select a whistle in the list, such as the whistle named "caret" shown in the image;
  4. select the NEW button;
  5. enter the frequencies in this format "[1]2000;" where "[1]" is the rank of a value at a 40th of a second, "2000" is the frequency in Hz, and ";" is a separator, you can use approximate values at first and then edit them later;
  6. select the DRAW button to show the contour, a red line indicates a change in frequency which may be too steep; the voltages graph is drawn in the screen accessed with the VOLTAGES... button;
  7. enter a name, such as "atest1" (the "a" will have it listed at the top of the whistles list for easy access), no double-quotes;
  8. Back out of the editor to the whistles list screen;
  9. Back out of the whistles list to the main screen;
  10. Type "atest1" in the emit field, no double-quotes;
  11. Select the EMIT button to emit the whistle on the tablet speaker or a connected speaker if any.

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.